{-# LANGUAGE NamedFieldPuns    #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

module Stack2nix
  ( Args(..)
  , stack2nix
  , version
  ) where

import           Control.Monad              (unless, void, when)
import           Data.Maybe                 (isJust)
import           Data.Monoid                ((<>))
import           Paths_stack2nix            (version)
import           Stack2nix.External.Stack
import           Stack2nix.External.Util    (runCmdFrom, failHard)
import           Stack2nix.External.VCS.Git (Command (..), ExternalCmd (..),
                                             InternalCmd (..), git)
import           Stack2nix.Types            (Args (..))
import           Stack2nix.Util
import           System.Directory           (doesFileExist,
                                             getCurrentDirectory, withCurrentDirectory)
import           System.Environment         (getEnv, setEnv)
import           System.FilePath            ((</>))
import           System.IO.Temp             (withSystemTempDirectory)

stack2nix :: Args -> IO ()
stack2nix args@Args{..} = do
  when argEnsureExecutables $ do
    ensureExecutableExists "cabal" "cabal-install"
    ensureExecutableExists "git" "git"
    ensureExecutableExists "nix-prefetch-git" "nix-prefetch-scripts"
  assertMinVer "git" "2"
  assertMinVer "cabal" "2"
  setEnv "GIT_QUIET" "y"
  updateCabalPackageIndex
  -- cwd <- getCurrentDirectory
  -- let projRoot = if isAbsolute argUri then argUri else cwd </> argUri
  let projRoot = argUri
  isLocalRepo <- doesFileExist $ projRoot </> argStackYaml
  logDebug args $ "stack2nix (isLocalRepo): " ++ show isLocalRepo
  logDebug args $ "stack2nix (projRoot): " ++ show projRoot
  logDebug args $ "stack2nix (argUri): " ++ show argUri
  if isLocalRepo
  then handleStackConfig Nothing projRoot
  else withSystemTempDirectory "s2n-" $ \tmpDir ->
    tryGit tmpDir >> handleStackConfig (Just argUri) tmpDir
  where
    updateCabalPackageIndex :: IO ()
    updateCabalPackageIndex = do
      home <- getEnv "HOME"
      out <- runCmdFrom home "cabal" ["update"]
      void $ failHard out

    tryGit :: FilePath -> IO ()
    tryGit tmpDir = do
      void $ git $ OutsideRepo $ Clone argUri tmpDir
      case argRev of
        Just r  -> void $ git $ InsideRepo tmpDir (Checkout r)
        Nothing -> return mempty

    handleStackConfig :: Maybe String -> FilePath -> IO ()
    handleStackConfig remoteUri localDir = do
      cwd <- getCurrentDirectory
      logDebug args $ "handleStackConfig (cwd): " ++ cwd
      logDebug args $ "handleStackConfig (localDir): " ++ localDir
      logDebug args $ "handleStackConfig (remoteUri): " ++ show remoteUri
      let stackFile = localDir </> argStackYaml
      alreadyExists <- doesFileExist stackFile
      unless alreadyExists $ error $ stackFile <> " does not exist. Use 'stack init' to create it."
      logDebug args $ "handleStackConfig (alreadyExists): " ++ show alreadyExists
      let go = if isJust remoteUri
               then withCurrentDirectory localDir
               else id
      go $ runPlan localDir remoteUri args