{-# LANGUAGE TemplateHaskell #-}

-- | Contains function to create repository.

module Life.Main.Init
       ( lifeInit
       , lifeInitQuestion
       ) where

import Path (mkRelFile)
import Path.IO (doesDirExist, doesFileExist)

import Life.Configuration (LifeConfiguration (..), parseHomeLife, renderLifeConfiguration,
                           singleFileConfig, writeGlobalLife)
import Life.Github (CopyDirection (..), Owner (..), Repo (Repo), copyLife, createRepository,
                    insideRepo)
import Life.Message (abortCmd, chooseYesNo, infoMessage, promptNonEmpty, skipMessage,
                     successMessage, warningMessage)
import Life.Shell (LifeExistence (..), createDirInHome, lifePath, relativeToHome, repoName,
                   whatIsLife)

import qualified Data.Set as Set

predefinedLifeConfig :: LifeConfiguration
predefinedLifeConfig = mempty
    { lifeConfigurationFiles = Set.fromList
          [ $(mkRelFile ".bash_profile")
          , $(mkRelFile ".profile")
          , $(mkRelFile ".vimrc")
          , $(mkRelFile ".emacs")
          , $(mkRelFile ".spacemacs")
          , $(mkRelFile ".gitconfig")
          , $(mkRelFile ".ghc/ghci.conf")
          , $(mkRelFile ".stylish-haskell.yaml")
          ]
    }

lifeInit :: Owner -> IO ()
lifeInit owner = whatIsLife >>= \case
    NoLife -> createLifeFile >>= createDotfilesDir
    OnlyLife _ -> askCreateLife >>= createDotfilesDir
    OnlyRepo _ -> abortCmd "init" "'~/dotfiles' directory already exist"  -- TODO: initialize .life from repo? :thinking_suicide:
    Both _ _ -> abortCmd "init" "'~/.life' file and '~/.dotfiles' directory are already initialized"
  where
    askCreateLife :: IO LifeConfiguration
    askCreateLife = do
        warningMessage ".life file is already exist."
        useIt <- chooseYesNo "Would you like to use it?"
        if useIt then parseHomeLife else createLifeFile

    createLifeFile :: IO LifeConfiguration
    createLifeFile = do
        infoMessage "Checking existence of some commonly used predefined files..."
        (exist, noExist) <- scanConfig predefinedLifeConfig

        unless (noExist == mempty) $ do
            infoMessage "The following files and directories weren't found; they won't be added to '~/.life' file:"
            skipMessage $ renderLifeConfiguration False noExist

        unless (exist == mempty) $ do
            infoMessage "Found the following files and directories:"
            successMessage $ renderLifeConfiguration False exist

        useDiscovered <- chooseYesNo "Would you like to add all discovered existing files and directories to .life configuration?"
        let lifeConfig = singleFileConfig lifePath <> (if useDiscovered then exist else mempty)

        infoMessage "Initializing global .life configuration file..."
        writeGlobalLife lifeConfig
        pure lifeConfig

    createDotfilesDir :: LifeConfiguration -> IO ()
    createDotfilesDir lifeConfig = do
        () <$ createDirInHome repoName
        insideRepo $ do
            copyLife FromHomeToRepo lifeConfig
            createRepository owner (Repo "dotfiles")

{- | Split given configuration into two:

1. All files and directories which exist on machine.
2. Other non-existing files and dirs.
-}
scanConfig :: LifeConfiguration -> IO (LifeConfiguration, LifeConfiguration)
scanConfig LifeConfiguration{..} = do
    (existingFiles, nonExistingFiles) <- partitionM (relativeToHome >=> doesFileExist) lifeConfigurationFiles
    (existingDirs, nonExistingDirs) <- partitionM (relativeToHome >=> doesDirExist) lifeConfigurationDirectories
    pure ( LifeConfiguration (Set.fromList existingFiles) (Set.fromList existingDirs)
         , LifeConfiguration (Set.fromList nonExistingFiles) (Set.fromList nonExistingDirs)
         )

partitionM :: forall f m a . (Monad m, Foldable f) => (a -> m Bool) -> f a -> m ([a], [a])
partitionM check = foldlM partitionAction ([], [])
  where
    partitionAction :: ([a], [a]) -> a -> m ([a], [a])
    partitionAction (ifTrue, ifFalse) a = check a >>= \case
        True  -> pure (a : ifTrue, ifFalse)
        False -> pure (ifTrue, a : ifFalse)


-- | If @.life@ and @dotfiles@ are not present you could want
-- to ask one if it needed to be initialised.
lifeInitQuestion :: Text  -- ^ Command name
                 -> IO () -- ^ Process to do
                 -> IO ()
lifeInitQuestion cmd process = do
    warningMessage ".life file and dotfiles/ do not exist"
    toInit <- chooseYesNo "Would you like to proceed initialization process?"
    if toInit then do
        infoMessage "Initialization process starts.."
        skipMessage "Insert your GitHub username:"
        owner <- promptNonEmpty
        lifeInit $ Owner owner
        process
    else abortCmd cmd "'~/.life' file is not initialized"