{-# LANGUAGE QuasiQuotes #-}

module Summoner.Source
       ( Source (..)
       , sourceT
       , fetchSource
       ) where

import Control.Arrow ((>>>))
import Control.Exception (catch)
import NeatInterpolation (text)
import System.Process (readProcess)
import Toml (BiMap, Key, TomlCodec)

import Summoner.Ansi (errorMessage, infoMessage)

import qualified Toml

data Source
    -- | URL link to the source file.
    = Url Text
    -- | File path to the local source file.
    | File FilePath
    -- | Link to external file.
    | Link Text
    deriving (Show, Eq)

matchUrl :: Source -> Maybe Text
matchUrl (Url url) = Just url
matchUrl _         = Nothing

matchFile :: Source -> Maybe FilePath
matchFile (File file) = Just file
matchFile _           = Nothing

matchLink :: Source -> Maybe Text
matchLink (Link link) = Just link
matchLink _           = Nothing

sourceT :: Key -> TomlCodec Source
sourceT nm = Toml.match (_Url  >>> Toml._Text)   (nm <> "url")
         <|> Toml.match (_File >>> Toml._String) (nm <> "file")
         <|> Toml.match (_Link >>> Toml._Text)   (nm <> "link")
  where
    _Url :: BiMap Source Text
    _Url = Toml.prism Url matchUrl

    _File :: BiMap Source FilePath
    _File = Toml.prism File matchFile

    _Link :: BiMap Source Text
    _Link = Toml.prism Link matchLink

fetchSource :: Bool -> Source -> IO (Maybe Text)
fetchSource isOffline = \case
    File path -> catch (Just <$> readFileText path) (fileError path)
    Url url   -> if isOffline
        then Nothing <$ infoMessage ("Ignoring fetching from URL in offline mode from source: " <> url)
        else fetchUrl url `catch` urlError url
    Link link -> putLink link
  where
    fileError :: FilePath -> SomeException -> IO (Maybe Text)
    fileError path _ = errorMessage ("Couldn't read file: " <> toText path)
                    >> pure Nothing

    urlError :: Text -> SomeException -> IO (Maybe Text)
    urlError url _ = errorMessage ("Couldn't get to link: " <> url)
                  >> pure Nothing

    fetchUrl :: Text -> IO (Maybe Text)
    fetchUrl url = Just . toText <$> readProcess "curl" [toString url] ""

    putLink :: Text -> IO (Maybe Text)
    putLink link = pure $ Just [text|See full content of the file [here]($link)|]