{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

module IO.Taskell where

import ClassyPrelude

import Data.FileEmbed   (embedFile)
import System.Directory (doesFileExist, getCurrentDirectory)

import Config             (usage, version)
import Data.Taskell.Lists (Lists, analyse, initial)

import           IO.Config         (Config, general, github, trello)
import           IO.Config.General (filename)
import qualified IO.Config.GitHub  as GitHub (token)
import qualified IO.Config.Trello  as Trello (token)

import IO.Markdown (parse, stringify)

import qualified IO.HTTP.GitHub as GitHub (GitHubIdentifier, getLists)
import qualified IO.HTTP.Trello as Trello (TrelloBoardID, getLists)

import UI.CLI (promptYN)

type ReaderConfig a = ReaderT Config IO a

data Next
    = Output Text
    | Load FilePath
           Lists
    | Exit

parseArgs :: [Text] -> ReaderConfig Next
parseArgs ["-v"]                   = pure $ Output version
parseArgs ["-h"]                   = pure $ Output usage
parseArgs ["-t", boardID, file]    = loadTrello boardID file
parseArgs ["-g", identifier, file] = loadGitHub identifier file
parseArgs ["-i", file]             = fileInfo file
parseArgs [file]                   = loadFile file
parseArgs []                       = (pack . filename . general <$> ask) >>= loadFile
parseArgs _                        = pure $ Output (unlines ["Invalid options", "", usage])

load :: ReaderConfig Next
load = getArgs >>= parseArgs

loadFile :: Text -> ReaderConfig Next
loadFile filepath = do
    mPath <- exists filepath
    case mPath of
        Nothing -> pure Exit
        Just path -> do
            content <- readData path
            pure $
                case content of
                    Right lists -> Load path lists
                    Left err    -> Output $ pack path <> ": " <> err

loadRemote :: (token -> FilePath -> ReaderConfig Next) -> token -> Text -> ReaderConfig Next
loadRemote createFn identifier filepath = do
    let path = unpack filepath
    exists' <- fileExists path
    if exists'
        then pure $ Output (filepath <> " already exists")
        else createFn identifier path

loadTrello :: Trello.TrelloBoardID -> Text -> ReaderConfig Next
loadTrello = loadRemote createTrello

loadGitHub :: GitHub.GitHubIdentifier -> Text -> ReaderConfig Next
loadGitHub = loadRemote createGitHub

fileInfo :: Text -> ReaderConfig Next
fileInfo filepath = do
    let path = unpack filepath
    exists' <- fileExists path
    if exists'
        then do
            content <- readData path
            pure $
                case content of
                    Right lists -> Output $ analyse filepath lists
                    Left err    -> Output $ pack path <> ": " <> err
        else pure Exit

createRemote ::
       (Config -> Maybe token)
    -> Text
    -> (token -> ReaderT token IO (Either Text Lists))
    -> token
    -> FilePath
    -> ReaderConfig Next
createRemote tokenFn missingToken getFn identifier path = do
    config <- ask
    let maybeToken = tokenFn config
    case maybeToken of
        Nothing -> pure $ Output missingToken
        Just token -> do
            lists <- lift $ runReaderT (getFn identifier) token
            case lists of
                Left txt -> pure $ Output txt
                Right ls -> do
                    create <- promptCreate path
                    if create
                        then do
                            lift $ writeData config ls path
                            pure $ Load path ls
                        else pure Exit

createTrello :: Trello.TrelloBoardID -> FilePath -> ReaderConfig Next
createTrello =
    createRemote
        (Trello.token . trello)
        (decodeUtf8 $(embedFile "templates/trello-token.txt"))
        Trello.getLists

createGitHub :: GitHub.GitHubIdentifier -> FilePath -> ReaderConfig Next
createGitHub =
    createRemote
        (GitHub.token . github)
        (decodeUtf8 $(embedFile "templates/github-token.txt"))
        GitHub.getLists

exists :: Text -> ReaderConfig (Maybe FilePath)
exists filepath = do
    let path = unpack filepath
    exists' <- fileExists path
    if exists'
        then pure $ Just path
        else do
            create <- promptCreate path
            if create
                then do
                    createPath path
                    pure $ Just path
                else pure Nothing

fileExists :: FilePath -> ReaderConfig Bool
fileExists path = lift $ doesFileExist path

promptCreate :: FilePath -> ReaderConfig Bool
promptCreate path = do
    cwd <- lift $ pack <$> getCurrentDirectory
    lift $ promptYN $ concat ["Create ", cwd, "/", pack path, "?"]

-- creates taskell file
createPath :: FilePath -> ReaderConfig ()
createPath path = do
    config <- ask
    lift (writeData config initial path)

-- writes Tasks to json file
writeData :: Config -> Lists -> FilePath -> IO ()
writeData config tasks path = void (writeFile path $ stringify config tasks)

-- reads json file
readData :: FilePath -> ReaderConfig (Either Text Lists)
readData path = do
    config <- ask
    content <- readFile path
    pure $ parse config content