module Deployer.Internal where

import           Check.Types
import           Compiler.Types
import           Data.List             (isPrefixOf)
import           Data.Text             (pack)
import           Deployer.Types
import           Shelly                (cp_r, fromText, shelly)
import           System.Directory      (getHomeDirectory,
                                        removeDirectoryRecursive, removeFile)
import           System.Environment    (getEnvironment)
import           System.FilePath       (normalise, (</>))
import           System.FilePath.Posix (dropFileName)
import           System.Posix.Files    (createSymbolicLink, removeLink)
import           Types
import           Utils

completeDeployments :: [Deployment] -> IO [Deployment]
completeDeployments ds = do
    home <- getHomeDirectory
    env <- getEnvironment
    case mapM (completeDeployment home env) ds of
        Left err -> die err
        Right fp -> return fp

type Environment = [(String, String)]
type HomeDir = FilePath

completeDeployment :: HomeDir -> Environment -> Deployment -> Either String Deployment
completeDeployment home env (Put srcs dst kind) = do
    csrcs <- mapM (complete home env) srcs
    cdst <- complete home env dst
    return $ Put csrcs cdst kind

complete :: HomeDir -> Environment -> FilePath -> Either String FilePath
complete home env fp = do
    let ids = parseId fp
    strs <- mapM (replaceId env) ids
    let completed = map (replaceHome home) strs
    return $ normalise $ concat completed

parseId :: FilePath -> [ID]
parseId [] = []
parseId ('$':'(':rest) = (Var id):(parseId next)
  where (id, (')':next)) = break (\c -> c == ')') rest
parseId (s:ss) = case parseId ss of
                    (Plain str):r   -> (Plain (s:str)):r
                    r               -> (Plain [s]):r

replaceId :: Environment -> ID -> Either String FilePath
replaceId _ (Plain str) = return str
replaceId e (Var str) = do
    case lookup str e of
        Nothing -> Left $ unwords ["variable", str, "could not be resolved from environment."]
        Just fp -> Right fp

replaceHome :: HomeDir -> FilePath -> FilePath
replaceHome home path
    = if "~" `isPrefixOf` path
        then home </> drop 2 path
        else path

performClean :: CleanupInstruction -> Sparker ()
performClean (CleanFile fp)         = incase conf_deploy_replace_files       $ rmFile fp
performClean (CleanDirectory fp)    = incase conf_deploy_replace_directories $ rmDir fp
performClean (CleanLink fp)         = incase conf_deploy_replace_links       $ unlink fp

unlink :: FilePath -> Sparker ()
unlink fp = liftIO $ removeLink fp

rmFile :: FilePath -> Sparker ()
rmFile fp = liftIO $ removeFile fp

rmDir :: FilePath -> Sparker ()
rmDir fp  = liftIO $ removeDirectoryRecursive fp


performDeployment :: Instruction -> IO ()
performDeployment (Instruction source destination LinkDeployment) = link source destination
performDeployment (Instruction source destination CopyDeployment) = copy source destination

copy :: FilePath -> FilePath -> IO ()
copy src dst = do
    createDirectoryIfMissing upperDir
    shelly $ cp_r (fromText $ pack src) (fromText $ pack dst)
  where upperDir = dropFileName dst

link :: FilePath -> FilePath -> IO ()
link src dst = do
    createDirectoryIfMissing upperDir
    createSymbolicLink src dst
  where upperDir = dropFileName dst