{-# LANGUAGE NamedFieldPuns #-}

module HsInspect.Search where

import BinIface
  ( CheckHiWay (..),
    TraceBinIFaceReading (..),
    readBinIface,
  )
import Control.Monad
import Control.Monad.IO.Class
import Data.Maybe
import Finder (findExposedPackageModule)
import qualified GHC
import GHC.PackageDb
import HsInspect.Sexp
import HscTypes (FindResult (..), ModIface (..))
import Json
import Module (ModLocation (..), Module (..), unitIdFS)
import PackageConfig
import Packages (LookupResult (..))
import TcRnMonad (initTcRnIf)

search :: GHC.GhcMonad m => String -> m [Hit]
search _query = do
  dflags <- GHC.getSessionDynFlags
  let Just dbs = GHC.pkgDatabase dflags
      pkgs = join (snd <$> dbs)
  join <$> traverse getHits pkgs

-- TODO Maybe haddock-html
getHits :: GHC.GhcMonad m => PackageConfig -> m [Hit]
getHits pkg = do
  results <- traverse finder (lookups pkg)
  join <$> traverse toHit results

-- FIXME finder doesn't work. It probably needs everything to be loaded. Just
-- traverse the library directories looking for .hi files and filter the modules
-- based on the exported list. That should avoid the risk of compiling anything.
toHit :: GHC.GhcMonad m => FindResult -> m [Hit]
toHit (Found (ModLocation _ hi _) _) = do
  env <- GHC.getSession
  iface <-
    liftIO $ initTcRnIf 'z' env () ()
      $ readBinIface IgnoreHiWay QuietBinIFaceReading hi
  pure [Hit . show . length $ mi_exports iface]
toHit _ = pure []

-- LookupFound Module PackageConfig
-- findLookupResult
-- findExposedPackageModule
-- lookupIfaceByModule
-- showIface
-- readBinIface
finder :: GHC.GhcMonad m => LookupResult -> m FindResult
finder lup = do
  env <- GHC.getSession
  liftIO $ findLookupResult env lup

-- TODO ghc should export Finder.findLookupResult
findLookupResult :: GHC.HscEnv -> LookupResult -> IO FindResult
findLookupResult env (LookupFound (Module mid name) _) =
  findExposedPackageModule env name (Just $ unitIdFS mid)
findLookupResult _ _ = error "not supported"

lookups :: PackageConfig -> [LookupResult]
lookups c@InstalledPackageInfo {exposedModules} =
  let modules = catMaybes $ snd <$> exposedModules
   in flip LookupFound c <$> modules

data Hit = Hit String

instance ToSexp Hit where
  toSexp (Hit txt) = toSexp txt

instance ToJson Hit where
  json (Hit _) = error "not implemented yet"