module Tintin.ConfigurationLoading
  ( ConfigurationLoading
  , loadInfo
  )
where

require Tintin.Errors
require Tintin.Capabilities.Logging
require Tintin.Capabilities.Filesystem
require Tintin.Domain.HtmlFile
require Tintin.Domain.Project

require Data.Text

import Tintin.Core
import qualified Universum.Unsafe as Unsafe
import Text.Read (read)


data ConfigurationLoading

loadInfo :: ( Has Logging.Capability eff
            , Has Filesystem.Capability eff
            )
         => [HtmlFile]
         -> Effectful eff Project.Info
loadInfo htmlFiles = do
  let pages = htmlFiles
              & map (\HtmlFile.HtmlFile {..} -> Project.Page title content filename)
  Filesystem.Path currentDir <- Filesystem.currentDirectory
  files <- Filesystem.list (Filesystem.Path currentDir)
  let packageYamlFile = find isPackageYaml files
  let cabalFile       = find isCabalFile files
  case packageYamlFile <|> cabalFile of
    Nothing -> do
      Errors.showAndDie ["No package.yaml or *.cabal file found."]
      error ""

    Just p -> do
      let tintinPath = Filesystem.Path $ currentDir <> "/.tintin.yml"
      Logging.debug "Reading project info"
      projectInfoFile <- Filesystem.readFile p
      tintinExists    <- Filesystem.doesExist tintinPath
      unless tintinExists $
        Filesystem.writeFile tintinPath "color: blue\n"
      tintinFile <- Filesystem.readFile tintinPath
      let
        projectName     = projectInfoFile & getFieldValue "name"
        projectSynopsis = projectInfoFile & getFieldValue "synopsis"
        projectGithub   = (projectInfoFile & getFieldValue "github")
                          <|> (projectInfoFile & getFieldValue "location")
        projectAuthor   = projectGithub & fmap getAuthor
        tintinColor     = tintinFile & getFieldValue "color"
        tintinLogo      = tintinFile & getFieldValue "logo"
      when (isNothing projectName) (Errors.showAndDie ["Project must have a name. Please set it in package.yaml or *.cabal."])
      when (isNothing projectSynopsis) (Errors.showAndDie ["Project must have a synopsis. Please set it in package.yaml or *.cabal."])
      when (isNothing tintinColor)
        (Errors.showAndDie [errorMessages])
      return Project.Info
        { name = Unsafe.fromJust projectName
        , synopsis = Unsafe.fromJust projectSynopsis
        , githubLink = parseGithubUrl <$> projectGithub
        , githubAuthor = projectAuthor
        , color = makeColor $ Unsafe.fromJust tintinColor
        , logoUrl = tintinLogo
        , pages = pages
        }

 where
  errorMessages = unlines
    ["Tintin usually generates a .tintin.yml file with a color configuration. Maybe you don't have enough permissions?"
    , ""
    , ""
    , "Try creating .tintin.yml and adding color:blue to it."
    ]
  isPackageYaml (Filesystem.Path p) =
    p == "package.yaml"

  isCabalFile   (Filesystem.Path p) =
    ".cabal" `Text.isInfixOf` p

  makeColor :: Text -> Project.Color
  makeColor txt =
    let capitalLetter = txt
                        & Text.head
                        & Text.singleton
                        & Text.toUpper
        restOfText    = txt
                        & Text.tail
    in  (capitalLetter <> restOfText)
         & toString
         & read

  getFieldValue field txt = txt
                          & lines
                          & filter (\t -> field `Text.isPrefixOf` Text.strip t)
                          & safeHead
                          & fmap Text.strip
                          & flatMap (Text.stripPrefix $ field <> ":")
                          & fmap Text.strip
  getAuthor txt =
    let unquoted = stripQuotes txt
    in parseGithubUrl unquoted
       & (\t -> if "http" `Text.isPrefixOf` t
                then Text.splitOn "/" t
                     & filter (not . Text.isInfixOf "git")
                     & filter (not . Text.null)
                     & Unsafe.tail
                     & Unsafe.head
                else t
         )
       & Text.takeWhile (/= '/')

  parseGithubUrl txt =
    let unquoted = stripGit $ stripQuotes txt
    in unquoted
       & Text.stripPrefix "github.com/"
       & fromMaybe unquoted

  stripQuotes txt =
    txt
    & Text.stripPrefix "\""
    & flatMap (Text.stripSuffix "\"")
    & fromMaybe txt

  stripGit txt =
    txt
    & Text.stripPrefix "git://"
    & flatMap (Text.stripSuffix ".git")
    & fromMaybe txt