{-# LANGUAGE CPP                 #-}
{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE TypeOperators       #-}

{-|
Module      : GHCup.Prelude
Description : MegaParsec utilities
Copyright   : (c) Julian Ospald, 2020
License     : LGPL-3.0
Maintainer  : hasufell@hasufell.de
Stability   : experimental
Portability : portable

GHCup specific prelude. Lots of Excepts functionality.
-}
module GHCup.Prelude
  (module GHCup.Prelude,
   module GHCup.Prelude.Internal,
#if defined(IS_WINDOWS)
   module GHCup.Prelude.Windows
#else
   module GHCup.Prelude.Posix
#endif
  )
where

import           GHCup.Errors
import           GHCup.Prelude.Internal
import           GHCup.Types.Optics   (HasLog)
import           GHCup.Prelude.Logger (logWarn)
#if defined(IS_WINDOWS)
import GHCup.Prelude.Windows
#else
import GHCup.Prelude.Posix
#endif

import           Control.Monad.IO.Class
import           Control.Monad.Reader
import           Haskus.Utils.Variant.Excepts
import           Text.PrettyPrint.HughesPJClass ( Pretty )
import qualified Data.Text                     as T
import System.Environment (getEnvironment)
import qualified Data.Map.Strict               as Map
import System.FilePath
import Data.List (intercalate)



-- for some obscure reason... this won't type-check if we move it to a different module
catchWarn :: forall es m env . ( Pretty (V es)
                             , HFErrorProject (V es)
                             , MonadReader env m
                             , HasLog env
                             , MonadIO m
                             , Monad m) => Excepts es m () -> Excepts '[] m ()
catchWarn = catchAllE @_ @es (\v -> lift $ logWarn (T.pack . prettyHFError $ v))


runBothE' :: forall e m a b .
             ( Monad m
             , Show (V e)
             , Pretty (V e)
             , HFErrorProject (V e)
             , PopVariant InstallSetError e
             , LiftVariant' e (InstallSetError ': e)
             , e :<< (InstallSetError ': e)
             )
          => Excepts e m a
          -> Excepts e m b
          -> Excepts (InstallSetError ': e) m ()
runBothE' a1 a2 = do
   r1 <- lift $ runE @e a1
   r2 <- lift $ runE @e a2
   case (r1, r2) of
      (VLeft e1, VLeft e2) -> throwE (InstallSetError e1 e2)
      (VLeft e , _       ) -> throwSomeE e
      (_       , VLeft e ) -> throwSomeE e
      (VRight _, VRight _) -> pure ()

-- "throwSomeE" function has been upstreamed in haskus-utils-variant-3.3
-- So, only conditionally include this shim if
-- haskus-utils-variant version is < 3.3

#if MIN_VERSION_haskus_utils_variant(3,3,0)
#else
-- | Throw some exception
throwSomeE :: forall es' es a m. (Monad m, LiftVariant es' es) => V es' -> Excepts es m a
{-# INLINABLE throwSomeE #-}
throwSomeE = Excepts . pure . VLeft . liftVariant
#endif

addToPath :: [FilePath]
          -> Bool         -- ^ if False will prepend
          -> IO [(String, String)]
addToPath paths append = do
 cEnv <- getEnvironment
 return $ addToPath' cEnv paths append

addToPath' :: [(String, String)]
          -> [FilePath]
          -> Bool         -- ^ if False will prepend
          -> [(String, String)]
addToPath' cEnv' newPaths append =
  let cEnv           = Map.fromList cEnv'
      paths          = ["PATH", "Path"]
      curPaths       = (\x -> maybe [] splitSearchPath (Map.lookup x cEnv)) =<< paths
      {- HLINT ignore "Redundant bracket" -}
      newPath        = intercalate [searchPathSeparator] (if append then (curPaths ++ newPaths) else (newPaths ++ curPaths))
      envWithoutPath = foldr (\x y -> Map.delete x y) cEnv paths
      pathVar        = if isWindows then "Path" else "PATH"
      envWithNewPath = Map.toList $ Map.insert pathVar newPath envWithoutPath
  in envWithNewPath