module Handler.Workflow.Instance.Update
  ( WorkflowInstanceUpdateButton(..)
  , workflowInstanceCanUpdate
  , postGWIUpdateR, postSWIUpdateR
  ) where

import Import
import Utils.Form
import Utils.Workflow

import Handler.Utils.Workflow.CanonicalRoute

import qualified Data.CaseInsensitive as CI

import qualified Data.Set as Set
import qualified Data.Map.Strict as Map

import Handler.Utils.Memcached


data WorkflowInstanceUpdateButton
  = BtnWorkflowInstanceUpdate
  deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable)
  deriving anyclass (Universe, Finite)

nullaryPathPiece ''WorkflowInstanceUpdateButton $ camelToPathPiece' 3
embedRenderMessage ''UniWorX ''WorkflowInstanceUpdateButton id

instance Button UniWorX WorkflowInstanceUpdateButton where
  btnClasses _ = [BCIsButton]


data WorkflowInstanceUpdateAction
  = WIUpdateGraph SharedWorkflowGraphId
  | WIUpdateCategory (Maybe WorkflowInstanceCategory)
  | WIUpdateInstanceDescription Lang (Maybe (Text, Maybe StoredMarkup))
  deriving (Eq, Ord, Read, Show, Generic, Typeable)


workflowInstanceUpdates :: WorkflowInstanceId
                        -> DB (Set WorkflowInstanceUpdateAction)
workflowInstanceUpdates wiId = execWriterT . maybeT_ $ do
  WorkflowInstance{..} <- MaybeT . lift $ get wiId
  wdId <- hoistMaybe workflowInstanceDefinition
  WorkflowDefinition{..} <- MaybeT . lift $ get wdId

  when (workflowDefinitionGraph /= workflowInstanceGraph) $
    tellPoint $ WIUpdateGraph workflowDefinitionGraph

  when (workflowDefinitionInstanceCategory /= workflowInstanceCategory) $
    tellPoint $ WIUpdateCategory workflowDefinitionInstanceCategory

  iDescs <- lift . lift $ selectList [WorkflowInstanceDescriptionInstance ==. wiId] []
  dDescs <- lift . lift $ selectList [WorkflowDefinitionInstanceDescriptionDefinition ==. wdId] []

  let iDescs' = Map.fromList $ map (\(Entity _ WorkflowInstanceDescription{..}) -> (CI.mk workflowInstanceDescriptionLanguage, (workflowInstanceDescriptionTitle, workflowInstanceDescriptionDescription))) iDescs
      dDescs' = Map.fromList $ map (\(Entity _ WorkflowDefinitionInstanceDescription{..}) -> (CI.mk workflowDefinitionInstanceDescriptionLanguage, (workflowDefinitionInstanceDescriptionTitle, workflowDefinitionInstanceDescriptionDescription))) dDescs

  forM_ (Map.keysSet iDescs' `Set.union` Map.keysSet dDescs') $ \lang -> if
    | Just iDesc <- Map.lookup lang iDescs'
    , Just dDesc <- Map.lookup lang dDescs'
    , iDesc /= dDesc
      -> tellPoint . WIUpdateInstanceDescription (CI.original lang) $ Just dDesc
    | Just dDesc <- Map.lookup lang dDescs'
    , not $ Map.member lang iDescs'
      -> tellPoint . WIUpdateInstanceDescription (CI.original lang) $ Just dDesc
    | Map.member lang iDescs'
    , not $ Map.member lang dDescs'
      -> tellPoint $ WIUpdateInstanceDescription (CI.original lang) Nothing
    | otherwise
      -> return ()

workflowInstanceCanUpdate :: WorkflowInstanceId
                          -> DB Bool
workflowInstanceCanUpdate wiId = not . null <$> workflowInstanceUpdates wiId


postGWIUpdateR :: WorkflowInstanceName -> Handler Void
postGWIUpdateR = updateR WSGlobal

postSWIUpdateR :: SchoolId -> WorkflowInstanceName -> Handler Void
postSWIUpdateR ssh = updateR $ WSSchool ssh


updateR :: RouteWorkflowScope -> WorkflowInstanceName -> Handler a
updateR rScope win = do
  runDB $ do
    scope <- maybeT notFound $ fromRouteWorkflowScope rScope
    wiId <- getKeyBy404 . UniqueWorkflowInstance win $ scope ^. _DBWorkflowScope 
    updates <- workflowInstanceUpdates wiId

    when (null updates) $
      addMessageI Warning MsgWorkflowInstanceUpdateNoActions

    forM_ updates $ \case
      WIUpdateGraph graphId -> do
        update wiId [ WorkflowInstanceGraph =. graphId ]
        addMessageI Success MsgWorkflowInstanceUpdateUpdatedGraph
      WIUpdateCategory iCat -> do
        update wiId [ WorkflowInstanceCategory =. iCat ]
        addMessageI Success MsgWorkflowInstanceUpdateUpdatedCategory
      WIUpdateInstanceDescription lang Nothing -> do
        deleteBy $ UniqueWorkflowInstanceDescription wiId lang
        addMessageI Success $ MsgWorkflowInstanceUpdateDeletedDescriptionLanguage lang
      WIUpdateInstanceDescription lang (Just (title, mDesc)) -> do
        void $ upsertBy
          (UniqueWorkflowInstanceDescription wiId lang) 
          WorkflowInstanceDescription
            { workflowInstanceDescriptionInstance = wiId
            , workflowInstanceDescriptionLanguage = lang
            , workflowInstanceDescriptionTitle = title
            , workflowInstanceDescriptionDescription = mDesc
            }
          [ WorkflowInstanceDescriptionTitle =. title
          , WorkflowInstanceDescriptionDescription =. mDesc
          ]
        addMessageI Success $ MsgWorkflowInstanceUpdateUpdatedDescriptionLanguage lang
    memcachedByInvalidate (AuthCacheWorkflowInstanceInitiators win rScope) $ Proxy @(Set (WorkflowRole UserId))
    memcachedByInvalidate (AuthCacheWorkflowInstanceWorkflowViewers win rScope) $ Proxy @(WorkflowWorkflowId, Set (WorkflowRole UserId))
    when (isTopWorkflowScope rScope) $
      memcachedByInvalidate NavCacheHaveTopWorkflowInstancesRoles $ Proxy @(Set ((RouteWorkflowScope, WorkflowInstanceName), WorkflowRole UserId))
        
  redirect $ _WorkflowScopeRoute # ( rScope, WorkflowInstanceListR )
