{-# LANGUAGE NoImplicitPrelude     #-}
{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NoFieldSelectors      #-}
{-# LANGUAGE OverloadedRecordDot   #-}
{-# LANGUAGE OverloadedStrings     #-}

{-|
Module      : Stack.SetupCmd
Description : Function related to Stack's @setup@ command.
License     : BSD-3-Clause

Function related to Stack's @setup@ command.
-}

module Stack.SetupCmd
  ( setupCmd
  ) where

import qualified Data.Either.Extra as EE
import           Stack.Prelude
import           Stack.Runners
                   ( ShouldReexec (..), withBuildConfig, withConfig )
import           Stack.Setup ( SetupOpts (..), ensureCompilerAndMsys )
import           Stack.Types.BuildConfig
                   ( HasBuildConfig, configFileL, wantedCompilerVersionL )
import           Stack.Types.CompilerPaths ( CompilerPaths (..) )
import           Stack.Types.Config ( Config (..), HasConfig (..) )
import           Stack.Types.GHCVariant ( HasGHCVariant )
import           Stack.Types.Runner ( Runner )
import           Stack.Types.SetupOpts ( SetupCmdOpts (..) )
import           Stack.Types.Version ( VersionCheck (..) )

-- | Function underlying the @stack setup@ command.
setupCmd :: SetupCmdOpts -> RIO Runner ()
setupCmd sco = withConfig YesReexec $ do
  installGHC <- view $ configL . to (.installGHC)
  installMsys <- view $ configL . to (.installMsys)
  case (installGHC, installMsys) of
    (True, True) -> withBuildConfig $ do
      (wantedCompiler, compilerCheck, mConfigFile) <-
        case sco.compilerVersion of
          Just v -> pure (v, MatchMinor, Nothing)
          Nothing -> do
           wantedCompilerVersion <- view wantedCompilerVersionL
           compilerCheck <- view (configL . to (.compilerCheck))
           configFile <- view configFileL
           -- We are indifferent as to whether the configuration file is a
           -- user-specific global or a project-level one.
           let eitherConfigFile = EE.fromEither configFile
           pure
             ( wantedCompilerVersion
             , compilerCheck
             , Just eitherConfigFile
             )
      setup sco wantedCompiler compilerCheck mConfigFile
    (False, True) -> warn
      [ styledNoInstallGHC
      , singleFlag
      ]
    (True, False) -> warn
      [ styledNoInstallMsys
      , singleFlag
      ]
    (False, False) -> warn
      [ styledNoInstallGHC
      , "and"
      , styledNoInstallMsys
      , flow "flags are"
      ]
 where
  styledNoInstallGHC = style Shell "--no-install-ghc"
  styledNoInstallMsys = style Shell "--no-install-msys"
  singleFlag = flow "flag is"
  warn docs = prettyWarnL $
       ["The"]
    <> docs
    <> [ flow "inconsistent with"
       , style Shell (flow "stack setup") <> "."
       , flow "No action taken."
       ]

setup ::
     (HasBuildConfig env, HasGHCVariant env)
  => SetupCmdOpts
  -> WantedCompiler
  -> VersionCheck
  -> Maybe (Path Abs File)
     -- ^ If we got the desired GHC version from that configuration file, which
     -- may be either a user-specific global or a project-level one.
  -> RIO env ()
setup sco wantedCompiler compilerCheck configFile = do
  config <- view configL
  sandboxedGhc <- (.sandboxed) . fst <$> ensureCompilerAndMsys SetupOpts
    { installGhcIfMissing = True
    , installMsysIfMissing = True
    , useSystem = config.systemGHC && not sco.forceReinstall
    , wantedCompiler
    , compilerCheck
    , configFile
    , forceReinstall = sco.forceReinstall
    , sanityCheck = True
    , skipGhcCheck = False
    , skipMsys = config.skipMsys
    , resolveMissingGHC = Nothing
    , ghcBindistURL = sco.ghcBindistUrl
    }
  let compiler = case wantedCompiler of
        WCGhc _ -> "GHC"
        WCGhcGit{} -> "GHC (built from source)"
        WCGhcjs {} -> "GHCJS"
      compilerHelpMsg = fillSep
        [ flow "To use this"
        , compiler
        , flow "and packages outside of a project, consider using:"
        , style Shell (flow "stack ghc") <> ","
        , style Shell (flow "stack ghci") <> ","
        , style Shell (flow "stack runghc") <> ","
        , "or"
        , style Shell (flow "stack exec") <> "."
        ]
  if sandboxedGhc
    then prettyInfoL
      [ flow "Stack will use a sandboxed"
      , compiler
      , flow "it installed."
      , compilerHelpMsg
      ]
    else prettyInfoL
      [ flow "Stack will use the"
      , compiler
      , flow "on your PATH. For more information on paths, see"
      , style Shell (flow "stack path")
      , "and"
      , style Shell (flow "stack exec env") <> "."
      , compilerHelpMsg
      ]
