{-# OPTIONS_GHC -fno-warn-orphans #-}

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell   #-}
{-# LANGUAGE TypeFamilies      #-}

-- | This module contains utility functions to work with shell.

-- TODO: rename this module to Life.Path ?

module Life.Shell
       ( -- * Constants
         lifePath
       , repoName

         -- * Functions
       , LifeExistence (..)
       , createDirInHome
       , relativeToHome
       , whatIsLife
       , ($|)
       ) where

import Path (Abs, Dir, File, Path, Rel, mkRelDir, mkRelFile, (</>))
import Path.IO (createDirIfMissing, doesDirExist, doesFileExist, getHomeDir)
import System.Process (callCommand, readProcess, showCommandForUser)

----------------------------------------------------------------------------
-- Global constants
----------------------------------------------------------------------------

-- | Name for life configuration file.
lifePath :: Path Rel File
lifePath = $(mkRelFile ".life")

-- TODO: consistent naming with @lifePath@ ?
-- | Default repository name for life configuration files.
repoName :: Path Rel Dir
repoName = $(mkRelDir "dotfiles/")

----------------------------------------------------------------------------
-- Shell interface
----------------------------------------------------------------------------

-- This is needed to be able to call commands by writing strings.
instance (a ~ Text, b ~ ()) => IsString ([a] -> IO b) where
    fromString cmd args = do
        let cmdStr = showCommandForUser cmd (map toString args)
        putStrLn $ "⚙  " ++ cmdStr
        callCommand cmdStr

-- | Run shell command with given options and return stdout of executed command.
infix 5 $|
($|) :: FilePath -> [Text] -> IO String
cmd $| args = readProcess cmd (map toString args) ""

-- | Creates directory with name "folder" under "~/folder".
createDirInHome :: Path Rel Dir -> IO (Path Abs Dir)
createDirInHome dirName = do
    newDir <- relativeToHome dirName
    newDir <$ createDirIfMissing False newDir

-- | Creates path relative to home directory
relativeToHome :: MonadIO m => Path Rel t -> m (Path Abs t)
relativeToHome path = do
    homeDir <- getHomeDir
    pure $ homeDir </> path

data LifeExistence
    = NoLife
    | OnlyLife (Path Abs File)
    | OnlyRepo (Path Abs Dir)
    | Both (Path Abs File) (Path Abs Dir)

whatIsLife :: IO LifeExistence
whatIsLife = do
    lifeFile <- relativeToHome lifePath
    repoDir  <- relativeToHome repoName

    isFile <- doesFileExist lifeFile
    isDir  <- doesDirExist  repoDir

    pure $ case (isFile, isDir) of
        (False, False) -> NoLife
        (True, False)  -> OnlyLife lifeFile
        (False, True)  -> OnlyRepo repoDir
        (True, True)   -> Both lifeFile repoDir