module Chromatin.Config(
  readConfig,
  analyzeConfig,
  RpluginModification(..),
  analyzeConfigIO,
) where

import Data.Maybe (fromMaybe)
import Data.Foldable (find)
import Text.ParserCombinators.Parsec
import System.FilePath (takeFileName)
import qualified Ribosome.Control.Ribo as Ribo (inspect)
import Ribosome.Config.Setting (setting)
import Chromatin.Data.Chromatin (Chromatin)
import qualified Chromatin.Data.Env as Env (rplugins)
import Chromatin.Data.Rplugin (Rplugin(Rplugin))
import Chromatin.Data.RpluginName (RpluginName(RpluginName))
import Chromatin.Data.RpluginSource (RpluginSource(..), HackageDepspec(HackageDepspec), PypiDepspec(PypiDepspec))
import Chromatin.Data.Rplugins (Rplugins(Rplugins))
import Chromatin.Data.RpluginConfig (RpluginConfig(RpluginConfig))
import qualified Chromatin.Settings as S (rplugins)

readConfig :: Chromatin Rplugins
readConfig = setting S.rplugins

data RpluginModification =
  RpluginNew RpluginName RpluginSource
  |
  RpluginRemove Rplugin
  |
  RpluginUpdate Rplugin RpluginSource
  |
  RpluginKeep Rplugin
  deriving (Eq, Show)

data ParsedSpec =
  PrefixedSpec String String
  |
  PlainSpec String

parsePrefixed :: GenParser Char st ParsedSpec
parsePrefixed = do
  prefix <- many $ noneOf ":"
  _ <- char ':'
  spec <- many anyChar
  return $ PrefixedSpec prefix spec

specParser :: GenParser Char st ParsedSpec
specParser = try parsePrefixed <|> fmap PlainSpec (many anyChar)

parseSpec :: String -> Either ParseError ParsedSpec
parseSpec = parse specParser "none"

sourceFromSpec :: String -> Either String RpluginSource
sourceFromSpec spec =
  case parseSpec spec of
    Right (PrefixedSpec prefix spec') -> case prefix of
      "pip" -> Right $ Pypi (PypiDepspec spec')
      "hackage" -> Right $ Hackage (HackageDepspec spec')
      "stack" -> Right $ Stack spec'
      a -> Left $ "unknown rplugin prefix `" ++ a ++ "` in `" ++ spec ++ "`"
    Right (PlainSpec name') ->
      Right $ Hackage (HackageDepspec name')
    Left _ -> Left $ "failed to parse `" ++ spec ++ "`"

rpluginHasName :: RpluginName -> Rplugin -> Bool
rpluginHasName target (Rplugin name _) = target == name

nameFromSource :: RpluginSource -> RpluginName
nameFromSource (Hackage (HackageDepspec n)) = RpluginName n
nameFromSource (Stack fp) = RpluginName (takeFileName fp)
nameFromSource (Pypi (PypiDepspec n)) = RpluginName n

modifyExisting :: RpluginSource -> Rplugin -> RpluginModification
modifyExisting _ _ = undefined

modification :: [Rplugin] -> RpluginConfig -> Either String RpluginModification
modification current (RpluginConfig spec explicitName) = do
  source <- sourceFromSpec spec
  let name = fromMaybe (nameFromSource source) explicitName
  let sameName = find (rpluginHasName name) current
  return $ maybe (RpluginNew name source) (modifyExisting source) sameName

removals :: [RpluginModification] -> [RpluginModification]
removals _ = []

analyzeConfig :: [Rplugin] -> Rplugins -> Either String [RpluginModification]
analyzeConfig current (Rplugins rplugins) = do
  let mods = traverse (modification current) rplugins
  fmap (\m -> m ++ removals m) mods

analyzeConfigIO :: Rplugins -> Chromatin (Either String [RpluginModification])
analyzeConfigIO rplugins = do
  current <- Ribo.inspect Env.rplugins
  return $ analyzeConfig current rplugins