{-# LANGUAGE ApplicativeDo   #-}
{-# LANGUAGE TemplateHaskell #-}

-- | Command line interface for @cuk@ executable.

module Cuk.Cli
       ( cuk
       ) where

import Data.Version (showVersion)
import Development.GitRev (gitCommitDate, gitDirty, gitHash)
import Options.Applicative (Parser, ParserInfo, argument, auto, command, execParser, fullDesc, help,
                            helper, info, infoOption, long, metavar, progDesc, short, strArgument,
                            subparser, switch)

import Cuk.ColorTerminal (arrow, blueCode, boldCode, redCode, resetCode)
import Cuk.Git (runCommit, runCurrent, runFix, runFresh, runHop, runNew, runPush, runResolve,
                runSync)
import Cuk.Issue (runIssue)

import qualified Data.Text as T
import qualified Paths_git_cuk as Meta (version)


cuk :: IO ()
cuk = execParser cliParser >>= \case
    Hop branchName -> runHop branchName
    Fresh branchName -> runFresh branchName
    New issueNum -> runNew issueNum
    Issue issueNum -> runIssue issueNum
    Commit message noIssue -> runCommit message noIssue
    Fix -> runFix
    Resolve branchName -> runResolve branchName
    Push isForce -> runPush isForce
    Sync -> runSync
    Current -> runCurrent >>= flip whenJust (runIssue . Just)

----------------------------------------------------------------------------
-- Parsers
----------------------------------------------------------------------------

-- | Main parser of the app.
cliParser :: ParserInfo CukCommand
cliParser = info ( helper <*> versionP <*> cukP )
    $ fullDesc <> progDesc "Haskell Git Helper Tool"

-- | Commands for
data CukCommand
    = Hop (Maybe Text)
    | Fresh (Maybe Text)
    | New Int
    | Issue (Maybe Int)
    | Commit Text Bool
    | Fix
    | Resolve (Maybe Text)
    | Push Bool
    | Sync
    | Current

-- | Commands parser.
cukP :: Parser CukCommand
cukP = subparser
    $ command "hop"     (info (helper <*> hopP)     $ progDesc "Switch to branch and sync it")
   <> command "fresh"   (info (helper <*> freshP)   $ progDesc "Rebase current branch on remote one")
   <> command "new"     (info (helper <*> newP)     $ progDesc "Create new branch from current one")
   <> command "commit"  (info (helper <*> commitP)  $ progDesc "Commit all local changes and prepend issue number")
   <> command "fix"     (info (helper <*> fixP)     $ progDesc "Ammend changes to the last commit and force push")
   <> command "issue"   (info (helper <*> issueP)   $ progDesc "Show the information about the issue")
   <> command "push"    (info (helper <*> pushP)    $ progDesc "Push the current branch")
   <> command "sync"    (info (helper <*> syncP)    $ progDesc "Sync local branch with its remote")
   <> command "resolve" (info (helper <*> resolveP) $ progDesc "Switch to master, sync and delete the branch")
   <> command "current" (info (helper <*> currentP) $ progDesc "Show info about current branch and issue (if applicable)")

hopP :: Parser CukCommand
hopP = Hop <$> maybeBranchP

freshP :: Parser CukCommand
freshP = Fresh <$> maybeBranchP

newP :: Parser CukCommand
newP = New <$> issueNumP

issueP :: Parser CukCommand
issueP = Issue <$> optional issueNumP

commitP :: Parser CukCommand
commitP = do
    msg <- strArgument (metavar "COMMIT_MESSAGE")
    noIssue <- switch
        $ long "no-issue"
       <> short 'n'
       <> help "Do not add [#ISSUE_NUMBER] prefix when specified"
    pure $ Commit msg noIssue

fixP :: Parser CukCommand
fixP = pure Fix

pushP :: Parser CukCommand
pushP = Push <$> switch
    ( long "force"
   <> short 'f'
   <> help "Force push"
    )

syncP :: Parser CukCommand
syncP = pure Sync

currentP :: Parser CukCommand
currentP = pure Current

resolveP :: Parser CukCommand
resolveP = Resolve <$> maybeBranchP

-- | Parse optional branch name as an argument.
maybeBranchP :: Parser (Maybe Text)
maybeBranchP = optional $ strArgument (metavar "BRANCH_NAME")

-- | Parse issue number as an argument.
issueNumP :: Parser Int
issueNumP = argument auto $ metavar "ISSUE_NUMBER"

-- | Show the version of the tool.
versionP :: Parser (a -> a)
versionP = infoOption cukVersion
    $ long "version"
   <> short 'v'
   <> help "Show cuk's version"

cukVersion :: String
cukVersion = toString
    $ T.intercalate "\n"
    $ [sVersion, sHash, sDate] ++ [sDirty | $(gitDirty)]
  where
    blueBold txt = blueCode <> boldCode <> txt <> resetCode
    sVersion = blueBold "Cuk " <> "v" <> toText (showVersion Meta.version)
    sHash = arrow <> blueBold "Git revision: " <> $(gitHash)
    sDate = arrow <> blueBold "Commit date:  " <> $(gitCommitDate)
    sDirty = redCode <> "There are non-committed files." <> resetCode