{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE LambdaCase #-}

-- | spawns a few processes that serve the que.run website
--
-- : exe que-website
--
-- : dep async
-- : dep config-ini
-- : dep process
-- : dep protolude
-- : dep req
module Run.Que.Website
  ( main
  )
where

import qualified Control.Concurrent.Async      as Async
import           Com.Simatime.Alpha
import qualified Data.ByteString.Char8         as BS
import qualified Data.Ini.Config               as Config
import qualified Data.Text                     as Text
import           Data.Text.Encoding             ( encodeUtf8 )
import qualified Data.Text.IO                  as Text
import           Network.HTTP.Req
import           Prelude                        ( error )
import qualified System.Directory              as Directory
import           System.Environment            as Environment
import qualified System.Exit                   as Exit
import           System.FilePath                ( (</>) )
import qualified System.Process                as Process

main :: IO ()
main = do
  (src, ns) <- Environment.getArgs >>= \case
    [src]     -> return (src, "_") -- default to _ ns which is special
    [src, ns] -> return (src, ns)
    _         -> Exit.die "usage: que-website <srcdir> [namespace]"
  home <- Directory.getHomeDirectory
  conf <- Text.readFile <| home </> ".config" </> "que.conf"
  let (Auth _ key) =
        either needConf identity
          <| Config.parseIniFile conf
          <| auth
          <| Text.pack ns
  putStrLn $ "serving " ++ src ++ " at " ++ ns
  run key (Text.pack ns) $ Sources { index      = src </> "index.md"
                                   , client     = src </> "client.py"
                                   , quescripts = src </> "quescripts.md"
                                   , style      = src </> "style.css"
                                   , apidocs    = src </> "apidocs.md"
                                   , tutorial   = src </> "tutorial.md"
                                   }

needConf :: error
needConf = error "you need a ~/.config/que.conf"

data Sources = Sources
  { index :: FilePath
  , quescripts :: FilePath
  , client :: FilePath
  , style :: FilePath
  , tutorial :: FilePath
  , apidocs :: FilePath
  }

type Namespace = Text
type Key = Text

data Auth = Auth Namespace Key

auth :: Text -> Config.IniParser Auth
auth ns = Config.section ns $ do
  key <- Config.field "key"
  return <| Auth ns key

run :: Key -> Text -> Sources -> IO ()
run key ns Sources {..} = Async.runConcurrently actions >> return ()
 where
  actions = traverse
    Async.Concurrently
    [ forever <| toHtml index >>= serve key ns "index"
    , forever <| toHtml quescripts >>= serve key ns "quescripts"
    , forever <| BS.readFile client >>= serve key ns "client"
    , forever <| toHtml tutorial >>= serve key ns "tutorial"
    , forever <| toHtml apidocs >>= serve key ns "apidocs"
    ]
  toHtml :: FilePath -> IO ByteString
  toHtml md =
    BS.pack
      <$> Process.readProcess
            "pandoc"
            [ "--self-contained"
            , "--css"
            , style
            , "-i"
            , md
            , "--from"
            , "markdown"
            , "--to"
            , "html"
            ]
            []

-- TODO: recover from 502 errors
serve :: Key -> Namespace -> Text -> ByteString -> IO ()
serve key ns path content = runReq defaultHttpConfig $ do
  let options =
        header "Authorization" (encodeUtf8 key) <> responseTimeout maxBound
  _ <- req POST
           (http "que.run" /: ns /: path)
           (ReqBodyBs content)
           ignoreResponse
           options
  liftIO $ return ()