{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, TemplateHaskell, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, GeneralizedNewtypeDeriving, FunctionalDependencies #-}
module Main where

import qualified Control.Monad.Catch as E
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import Control.Monad.Reader
import qualified Control.Monad.Log as Log
import Control.Lens
import qualified Network.Wai.Handler.Warp as Warp

import Data.Semigroup ((<>))
import Options.Applicative
import Options.Applicative.Text
import Options.Applicative.Helper

import qualified Distribution.Skete.Storage.Interface as Storage
import qualified Distribution.Skete.Storage.GitFat as GitFat
import Distribution.Skete.Haskell.Package

import SketeUpdate

data CommandOptions =
    COImport {
      _coUpdateConf :: UpdateConfig
    , _coRepo :: T.Text
    }
  | COServe {
      _coPort :: Warp.Port
    , _coRepo :: T.Text
    }
--  | CODocs {
--      _coPackageVersion :: Storage.PackageVersion
--    , _coPackageSet :: Storage.PackageSet
--    , _coRepo :: T.Text
--    }
  deriving (Read, Show, Eq, Ord)

makeClassy ''CommandOptions

commandParser :: Parser CommandOptions
commandParser =
    subconcat [
      parseImport
    , parseServe
--    , parseDocs
    ]
  where
    parsePackageSet =
      textOption $
           long "set"
        <> metavar "PACKAGESET"
        <> showDefault
        <> help "The PackageSet to import the new PackageVersions into."
        <> value "all"

    parseRepo =
      textOption $
           long "repo"
        <> metavar "GITDIR"
        <> help "The location of the skete store."
        <> value "./.git"

    parseImport =
      command "import" $
        infoHelper `flip` (fpDesc "import") $
          COImport <$> (UpdateConfig <$>
              textOption (
                   long "upstream"
                <> metavar "HACKAGE"
                <> showDefault
                <> help "A schema+host for a Hackage-like service to import PackageVersions from."
                <> value "https://hackage.haskell.org" )
          <*> parsePackageSet
          <*> ((Just <$> strOption (
                     long "cache"
                  <> metavar "CACHEDIR"
                  <> help "A location to find predownloaded tarballs and save tarballs as they're downloaded." ))
              <|> (pure Nothing)))
          <*> parseRepo

    parseServe =
      command "serve" $
        infoHelper `flip` (fpDesc "import") $
          COServe <$>
              option auto (
                   long "port"
                <> metavar "PORT"
                <> showDefault
                <> help "Which port to provide a subset of the Hackage API on."
                <> value 8081 )
          <*> parseRepo

--    parseDocs = Prelude.error "doc parser not completed"

data CommandConfig =
    CCImport {
      _ccUpConf :: UpdateConfig
    }
--  | CCServe {
--      _ccPort :: Warp.Port
--    }
  deriving (Show, Eq, Ord)

makeClassy ''CommandConfig

instance HasUpdateConfig CommandConfig where
  updateConfig = ccUpConf

data WrappedConfig c =
  WrappedConfig {
    _wcLoggerConf :: Log.Logger ()
  , _wcComConf :: c
  }

makeClassy ''WrappedConfig

instance HasUpdateConfig (WrappedConfig UpdateConfig) where
  updateConfig = wcComConf

newtype SketeToolT c m a =
    SketeToolT { runSketeToolT :: ReaderT (WrappedConfig c) m a }
  deriving ( Functor, Applicative, Monad, MonadIO, E.MonadThrow, E.MonadCatch
           , MonadTrans, MonadReader (WrappedConfig c) )

instance MonadIO m => Log.MonadLog () (SketeToolT c m) where
    askLogger = view wcLoggerConf
    localLogger f m = local (over wcLoggerConf f) m

main :: IO ()
main = do
    logConf <- Log.makeDefaultLogger
              Log.simpleTimeFormat (Log.LogStderr 0) (Log.Level 0) ()
    --Log.runLogTSafe logConf . Log.debug . mconcat $ ["Logger created"]
    cmd <- helperExecParser commandParser
          (fpDesc "skete-haskell, the skete Haskell package manager.")
    (Storage.storage (GitFat.GFC . TE.encodeUtf8 $ (cmd ^. coRepo))::GitFat.GitFat p a -> IO a) $ do
         case cmd of
           COImport {_coUpdateConf=upConf} ->
             (runReaderT `flip` (WrappedConfig logConf upConf)) . runSketeToolT $ do
                    fullUpdate
           _ -> liftIO . print $ cmd


{-
    view timeBetweenUpdates >>= delay
    hackageNewEvents
-}
