module Development.IDE.Plugin.CodeAction.Rules
  ( rulePackageExports
  )
where

import           Data.HashMap.Strict            ( fromListWith )
import           Data.Text                      ( Text
                                                , pack
                                                )
import           Data.Traversable               ( forM )
import           Development.IDE.Core.Rules
import           Development.IDE.GHC.Util
import           Development.IDE.Plugin.CodeAction.RuleTypes
import           Development.Shake
import           GHC                            ( DynFlags(pkgState) )
import           HscTypes                       ( IfaceExport
                                                , hsc_dflags
                                                , mi_exports
                                                )
import           LoadIface
import           Maybes
import           Module                         ( Module(..)
                                                , ModuleName
                                                , moduleNameString
                                                )
import           Packages                       ( explicitPackages
                                                , exposedModules
                                                , packageConfigId
                                                )
import           TcRnMonad                      ( WhereFrom(ImportByUser)
                                                , initIfaceLoad
                                                )

rulePackageExports :: Rules ()
rulePackageExports = defineNoFile $ \(PackageExports session) -> do
  let env     = hscEnv session
      pkgst   = pkgState (hsc_dflags env)
      depends = explicitPackages pkgst
      targets =
        [ (pkg, mn)
        | d        <- depends
        , Just pkg <- [lookupPackageConfig d env]
        , (mn, _)  <- exposedModules pkg
        ]

  results <- forM targets $ \(pkg, mn) -> do
    modIface <- liftIO $ initIfaceLoad env $ loadInterface
      ""
      (Module (packageConfigId pkg) mn)
      (ImportByUser False)
    case modIface of
      Failed    _err -> return mempty
      Succeeded mi   -> do
        let avails = mi_exports mi
        return $ concatMap (unpackAvail mn) avails
  return $ fromListWith (++) $ concat results

unpackAvail :: ModuleName -> IfaceExport -> [(Text, [(IdentInfo, Text)])]
unpackAvail mod =
  map (\id@IdentInfo {..} -> (name, [(id, pack $ moduleNameString mod)]))
    . mkIdentInfos