content: {-# LANGUAGE AllowAmbiguousTypes #-}
  
  module Ema.Site (
    EmaSite (..),
    EmaStaticSite,
  ) where
  
  import Control.Monad.Logger (MonadLoggerIO)
  import Ema.Asset (Asset)
  import Ema.CLI qualified as CLI
  import Ema.Dynamic (Dynamic)
  import Ema.Route.Class (IsRoute (RouteModel))
  import Optics.Core (Prism')
  import UnliftIO (MonadUnliftIO)
  
  {- | Typeclass to orchestrate an Ema site
  
    Given a route `r` from the class of `IsRoute` types, instantiating EmaSite
    on it enables defining the site build pipeline as follows:
  
    @
    SiteArg -> siteInput -> Dynamic model --[r, model]--> siteOutput
    @
  
    - `SiteArg` is typically not used, but it can be used to pass command line
    arguments and other such settings.
    - `siteInput` returns a time-varying value (Dynamic) representing the data for
    your static site.
    - `siteOutput` takes this data model (oneshot value) and returns the generated
    content (usually HTML asset, per `SiteOutput`) for the given route.
  
    Finally, `Ema.App.runSite @r arg` (where `arg` is of type `SiteArg`) is run
    from the `main` entry point to run your Ema site.
  -}
  class (IsRoute r) => EmaSite r where
    -- | `SiteArg` is typically settings from the environment (config file, or
    --    command-line arguments) that your Dynamic-producing `siteInput` function
    --    consumes as argument.
    type SiteArg r :: Type
  
    -- By default Ema sites have no site arguments.
    type SiteArg r = ()
  
    -- | Type of the value returned by `siteOutput`. Usually `Asset LByteString`
    -- but it can be anything.
    type SiteOutput r :: Type
  
    type SiteOutput r = Asset LByteString
  
    -- | Get the model's time-varying value as a `Dynamic`.
    --
    --    If your model is not time-varying, use `pure` to produce a constant value.
    siteInput ::
      forall m.
      (MonadIO m, MonadUnliftIO m, MonadLoggerIO m) =>
      CLI.Action ->
      -- | The value passed by the programmer to `Ema.App.runSite`
      SiteArg r ->
      -- | Time-varying value of the model. If your model is not time-varying, use
      -- `pure` to produce a constant value.
      m (Dynamic m (RouteModel r))
  
    -- | Return the output (typically an `Asset`) for the given route and model.
    siteOutput ::
      forall m.
      (MonadIO m, MonadLoggerIO m) =>
      Prism' FilePath r ->
      RouteModel r ->
      r ->
      m (SiteOutput r)
  
  -- | Like `EmaSite` but `SiteOutput` is a bytestring `Asset`.
  type EmaStaticSite r = (EmaSite r, SiteOutput r ~ Asset LByteString)
  