module HsInspect.LSP.Util where

import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Except (ExceptT(..))
import Data.List (intercalate)
import qualified Data.List as L
import System.Directory (listDirectory)
import System.Environment (getEnvironment)
import System.Exit (ExitCode(..))
import System.FilePath
import qualified System.Log.Logger as L
import qualified System.Process as P

-- the first parent directory where a file or directory name matches the predicate
locateDominatingDir :: (String -> Bool) -> FilePath -> IO (Maybe FilePath)
locateDominatingDir p dir = do
  file' <- locateDominatingFile p dir
  pure $ takeDirectory <$> file'

-- same as locateDominating but returns the first file that matches the predicate
locateDominatingFile :: (String -> Bool) -> FilePath -> IO (Maybe FilePath)
locateDominatingFile p dir = do
  files <- listDirectory dir
  let parent = takeDirectory dir
  case L.find p $ takeFileName <$> files of
    Just file -> pure . Just $ dir </> file
    Nothing ->
      if parent == dir
       then pure Nothing
       else locateDominatingFile p parent

shell :: String -> [String] -> Maybe FilePath -> Maybe String -> [(String, String)] -> ExceptT String IO String
shell command args cwd path env_extra = ExceptT $ do
  env <- getEnvironment
  let env' = maybe env (\p -> ("PATH", p) : env) path
      process = (P.proc command args) { P.env = Just (env_extra <> env')
                                      , P.cwd = cwd }
  liftIO $ L.debugM "haskell-lsp" $ "hsinspect-lsp:shell:" <> command <> " " <> intercalate " " args
  (code, stdout, stderr) <- P.readCreateProcessWithExitCode process ""
  case code of
    ExitFailure i -> pure . Left $
      concat [ "exit code: ", show i
             , "\n stdout: ", stdout
             , "\n stderr: ", stderr ]
    ExitSuccess -> pure $ Right stdout