content: {-|
  Module: IHP.Job.Dashboard.Types
  Description:  Types for Job dashboard
  -}
  {-# LANGUAGE AllowAmbiguousTypes #-}
  module IHP.Job.Dashboard.Types (
      BaseJob(..),
      JobsDashboardController(..),
      TableViewable(..),
      IncludeWrapper(..),
  ) where
  
  import IHP.Prelude
  import IHP.ControllerPrelude
  import IHP.ViewPrelude (Html)
  import IHP.RouterPrelude hiding (get, tshow, error, map, putStrLn, elem)
  import Database.PostgreSQL.Simple.FromRow (FromRow(..), field)
  import IHP.Job.Queue () -- get FromField definition for JobStatus
  
  
  data BaseJob = BaseJob {
      table :: Text
    , id :: UUID
    , status :: JobStatus
    , updatedAt :: UTCTime
    , createdAt :: UTCTime
    , lastError :: Maybe Text
  } deriving (Show)
  
  class TableViewable a where
      -- | Human readable title displayed on the table
      tableTitle :: Text
  
      -- | Database table backing the view
      modelTableName :: Text
  
      tableHeaders :: [Text]
      renderTableRow :: a -> Html
  
      -- | Link used in the table to send user to new job form
      newJobLink :: Html
  
      -- | Gets records for displaying in the dashboard index page
      getIndex :: (?context :: ControllerContext, ?modelContext :: ModelContext) => IO [a]
  
      -- | Gets paginated records for displaying in the list page
      getPage :: (?context :: ControllerContext, ?modelContext :: ModelContext) => Int -> Int -> IO [a]
  
  instance FromRow BaseJob where
      fromRow = BaseJob <$> field <*> field <*> field <*> field <*> field <*> field
  
  -- | Often, jobs are related to some model type. These relations are modeled through the type system.
  -- For example, the type 'Include "userId" UpdateUserJob' models an 'UpdateUserJob' type that can access
  -- the 'User' it belongs to through the 'userId' field.
  -- For some reason, GHC doesn't allow us to create implementations of type family applications, so the following doesn't work:
  --
  -- > instance DisplayableJob (Include "userId" UpdateUserJob) where
  --
  -- However, if we wrap this in a concrete type, it works fine. That's what this wrapper is for.
  -- To get the same behavior as above, just define
  --
  -- > instance DisplayableJob (IncludeWrapper "userId" UpdateUserJob) where
  --
  -- and wrap the values as so:
  --
  -- > jobsWithUsers <- query @UpdateUserJob
  -- >    |> fetch
  -- >    >>= mapM (fetchRelated #userId)
  -- >    >>= pure . map (IncludeWrapper @"userId" @UpdateUserJob)
  newtype IncludeWrapper (id :: Symbol) job = IncludeWrapper (Include id job)
  
  -- | Defines controller actions for acting on a dashboard made of some list of types.
  -- Later functions and typeclasses introduce constraints on the types in this list,
  -- so you'll get a compile error if you try and include a type that is not a job.
  data JobsDashboardController authType (jobs :: [Type])
      = ListJobsAction
      | ListJobAction { jobTableName :: Text, page :: Int }
      -- These actions are used for 'pathTo'. Need  to pass the parameters explicity to know how to build the path
      | ViewJobAction { jobTableName :: Text, jobId :: UUID }
      | CreateJobAction { jobTableName :: Text }
      | DeleteJobAction { jobTableName :: Text, jobId :: UUID }
      | RetryJobAction { jobTableName :: Text, jobId :: UUID }
  
      -- These actions are used for interal routing. Parameters are extracted dynamically in the action based on types
      | ListJobAction'
      | ViewJobAction'
      | CreateJobAction'
      | DeleteJobAction'
      | RetryJobAction'
      deriving (Show, Eq, Data)
  
  
  instance HasPath (JobsDashboardController authType jobs) where
      pathTo ListJobsAction = "/jobs/ListJobs"
      pathTo ListJobAction   { .. } = "/jobs/ListJob?tableName=" <> jobTableName <> "&page=" <> tshow page
      pathTo ViewJobAction   { .. } = "/jobs/ViewJob?tableName=" <> jobTableName <> "&id=" <> tshow jobId
      pathTo CreateJobAction { .. } = "/jobs/CreateJob?tableName=" <> jobTableName
      pathTo DeleteJobAction { .. } = "/jobs/DeleteJob?tableName=" <> jobTableName <> "&id=" <> tshow jobId
      pathTo RetryJobAction  { .. } = "/jobs/RetryJob?tableName=" <> jobTableName <> "&id=" <> tshow jobId
      pathTo _ = error "pathTo for internal JobsDashboard functions not supported. Use non-backtick action and pass necessary parameters to use pathTo."
  
  instance CanRoute (JobsDashboardController authType jobs) where
      parseRoute' = do
          (string "/jobs" <* endOfInput >> pure ListJobsAction)
          <|> (string "/jobs/" <* endOfInput >> pure ListJobsAction)
          <|> (string "/jobs/ListJobs" <* endOfInput >> pure ListJobsAction)
          <|> (string "/jobs/ListJob" <* endOfInput >> pure ListJobAction')
          <|> (string "/jobs/ViewJob" <* endOfInput >> pure ViewJobAction')
          <|> (string "/jobs/CreateJob" <* endOfInput >> pure CreateJobAction')
          <|> (string "/jobs/DeleteJob" <* endOfInput >> pure DeleteJobAction')
          <|> (string "/jobs/RetryJob" <* endOfInput >> pure RetryJobAction')
  
  