{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_HADDOCK show-extensions #-}

{-|
Module      : $Header$
Copyright   : (c) 2016 Deakin Software & Technology Innovation Lab
License     : BSD3

Maintainer  : Rhys Adams <rhys.adams@deakin.edu.au>
Stability   : unstable
Portability : portable

"Eclogues.API" documentation via "Servant.Docs" and associates.
-}

module Eclogues.ApiDocs (APIWithDocs, apiDocs, apiDocsMd) where

import Eclogues.Prelude hiding (fromString)
import Eclogues.API (AbsoluteURI, Action, API, DependencyPatch, parseAbsoluteURI)
import Eclogues.Job
  ( Stage (Failed, Running), FailureReason (..), Satisfiability (Satisfiable)
  , Sealed (..), NonEmptySet, singleton )
import qualified Eclogues.Job as Job

import qualified Data.HashMap.Strict as HM
import Data.Text (pack, unpack)
import Data.UUID (fromString, nil)
import Servant.API (Capture, Get, PlainText, (:>), (:<|>))
import qualified Servant.Docs as Docs
import Servant.Docs
  ( ToCapture (..), ToSample (..), DocCapture (..)
  , docs, markdown, samples, noSamples )
import Servant.Streaming.Docs ()

instance ToCapture (Capture "id" Job.Name) where
    toCapture _ = DocCapture "id" "job name"

instance ToCapture (Capture "id" Job.ContainerId) where
    toCapture _ = DocCapture "id" "container id"

instance ToCapture (Capture "filename" Job.FileId) where
    toCapture _ = DocCapture "filename" "file name"

instance ToSample Job.Stage where
    toSamples _ = [("A running task", Running)
                  ,("A task killed by a user", Failed UserKilled)]

instance ToSample Job.BoxSpec where
    toSamples _ = fmap Job.mkBoxSpec <$> toSamples (Proxy :: Proxy Job.Name)

instance ToSample Job.BoxStatus where
    toSamples _ = [("An open, empty box", Job.mkBoxStatus (assumedName "empty") Unsealed mempty)
                  ,("A sealed box", Job.mkBoxStatus (assumedName "full") Sealed (HM.fromList [("config.txt", nil), ("input.dat", u2)]))]
      where u2 = assumed "full uuid" $ fromString "c2cc10e1-57d6-4b6f-9899-38d972112d8c"

instance ToSample Job.Dependencies where
    toSamples _ = [ ("No dependencies", mempty)
                  , ("A required dependency",  Job.Dependencies $ HM.singleton helloName False)
                  , ("An optional dependency", Job.Dependencies $ HM.singleton helloName True) ]

instance ToSample Job.AnySpec
instance ToSample Job.AnyStatus
instance ToSample DependencyPatch
instance ToSample Action

instance ToSample AbsoluteURI where
    toSamples _ = [("An arbitrary absolute URI", assumed "absolute URI" $ parseAbsoluteURI "http://example.com/entity")]

instance (ToSample a) => ToSample (NonEmptySet a) where
    toSamples _ = fmap singleton <$> toSamples (Proxy :: Proxy a)

assumed :: String -> Maybe a -> a
assumed n = fromMaybe (error $ "bad " <> n)

assumedName :: Text -> Job.Name
assumedName n = assumed ("name: " ++ unpack n) $ Job.mkName n

helloName :: Job.Name
helloName = assumedName "hello"

spec :: Job.Spec
spec = Job.mkSpec helloName con ["-c", "echo hello world > hello.txt"] minBound False mempty
  where con = Job.ContainerId $ assumedName "bash"

depSpec :: Job.Spec
depSpec = Job.mkSpec n con ["virgil-dependencies/hello/hello.txt"] minBound True dep
  where
    n = assumedName "cat-hello"
    con = Job.ContainerId $ assumedName "cat"
    dep = Job.Dependencies $ HM.singleton helloName False

instance ToSample Job.Spec where
    toSamples _ = [("A job with output", spec)
                  ,("A job depending on previous output", depSpec)]

instance ToSample Job.InputSpec where
    toSamples _ = fmap Job.inputSpec <$> toSamples (Proxy :: Proxy Job.Spec)

failedSpec :: Job.Status
failedSpec = Job.mkStatus deadSpec (Failed $ NonZeroExitCode 1) Satisfiable nil mempty where
    deadSpec = Job.mkSpec n con [] minBound False mempty
    n = assumedName "i-fail"
    con = Job.ContainerId $ assumedName "false"

instance ToSample Job.Status where
    toSamples _ = samples [ failedSpec
                          , Job.mkStatus (spec & Job.container .~ con & Job.arguments .~ ["world"]) (Failed TimeExceeded) Satisfiable nil mempty
                          , Job.mkStatus depSpec (Failed $ DependencyFailed helloName) Satisfiable nil mempty ]
      where con = Job.ContainerId $ assumedName "my_hello_container"

instance ToSample Job.Name where
    toSamples _ = samples [assumedName "hello-world"]

deriving instance ToSample Job.ContainerId

instance ToSample () where
    toSamples _ = noSamples

apiDocs :: Docs.API
apiDocs = docs (Proxy :: Proxy API)

apiDocsMd :: Text
apiDocsMd = pack $ markdown apiDocs

type APIWithDocs = API :<|> "api" :> Get '[PlainText] Text
