{-# LANGUAGE OverloadedStrings #-}

module Web.JobsUi.Actions where

import Web.JobsUi.Internal.Types
import Web.JobsUi.Html
import Web.JobsUi.Forms

import Data.Foldable
import Control.Monad.Trans
import Web.Spock
import Web.Spock.Lucid
import Web.Spock.Digestive
import qualified Data.Text as T
import qualified Data.Set as S
import Control.Concurrent.STM
import Data.Time
import qualified Data.Sequence as Seq
import qualified Lucid as H
import Network.HTTP.Types.Status

-------------
-- Actions --
-------------

myError :: MonadIO a => Status  -> ActionCtxT () a ()
myError = \case
  Status 404 msg ->
    lucid $ template "404 - Not found." $ do
      H.h3_ "Not Found"
      H.toHtml msg
  Status 500 msg ->
    lucid $ template "500 - Internal error." $ do
      H.h3_ "Internal Error"
      H.toHtml msg
  Status cod msg ->
    lucid $ template (T.pack $ show cod) $ H.toHtml msg

showHistory :: Action () ()
showHistory = do
  jobs <- getState >>= liftIO . readTVarIO . myjobsVar
  lucid $ template "Welcome" $ do
    displayJobsList jobs
    for_ (running jobs) (displayJob Running)

showJob :: JobId -> Action () ()
showJob i = do
  jobs <- getState >>= liftIO . readTVarIO . myjobsVar
  lucid $ template (T.pack $ "Job #" <> show i) $ do
    displayJobsList jobs
    for_ (find ((==) i . jobId) $ waiting jobs) $ displayJob Waiting
    for_ (find ((==) i . jobId) $ running jobs) $ displayJob Running
    for_ (find ((==) i . jobId) $ done jobs) $ displayJob Done

jobsMenu :: S.Set T.Text -> Action () ()
jobsMenu jobtypes = do
  lucid $ template "Jobs Menu" $
    H.ul_ $ forM_ jobtypes $ \jobtype -> do
      H.li_ $ H.a_ [ H.href_ $ "/job/create/" <> jobtype ] (H.toHtml jobtype)

createJob :: JobInfo info -> Action () ()
createJob jobinfo = do

  let
    -- | Display the form to the user
    formView mErr view = do
      form <- secureForm (jiType jobinfo) (editJobFormView jobinfo) view
      formViewer "create" form mErr

  -- Run the form
  form <- runForm "" (editJobForm $ Left jobinfo)
  -- validate the form.
  -- Nothing means failure. will display the form view back to the user when validation fails.
  case form of
    (view, Nothing) ->
      formView Nothing view

    (_, Just EditJob{..}) -> do
      ServerState{myjobsVar, counterVar} <- getState
      time <- liftIO getZonedTime
      dat <- liftIO $ jiConstructor ejJobInfo ejPayload
      jid <- liftIO $ atomically $ do
        counter <- readTVar counterVar
        myjobs <- readTVar myjobsVar
        writeTVar counterVar $ counter + 1
        writeTVar myjobsVar $ myjobs
          { waiting = flip (Seq.<|) (waiting myjobs) $ Job
            { jobId = JobId counter
            , jobTimeQueued = time
            , jobTimeStarted = Nothing
            , jobTimeEnded = Nothing
            , jobPayload = dat
            , jobInfo = ejJobInfo
            , jobFinished = Nothing
            , jobThread = Nothing
            }
          }
        pure counter
      redirect $ "/job/" <> T.pack (show jid)

-- | Display the form to the user
formViewer :: T.Text -> Html -> Maybe Html -> Action v ()
formViewer actionName form mErr = do
  lucid $ do
    template
      actionName
      $ do
        maybe (pure ()) id mErr
        form