content: {-# LANGUAGE AllowAmbiguousTypes #-}
  {-# LANGUAGE DeriveAnyClass #-}
  {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  module Ema.Route.Generic.Example where
  
  import Data.Generics.Sum.Any (AsAny (_As))
  import Data.SOP (I (..), NP (..))
  import Ema
  import Ema.Asset qualified as Asset
  import Ema.Route.Generic
  import Ema.Route.Generic.TH
  import Generics.SOP qualified as SOP
  import Generics.SOP.TH qualified as SOP
  import Optics.Core ((%))
  import Optics.Prism (prism')
  
  -- ----------
  -- Examples
  -- ----------
  
  type M = (Int, Int, String)
  
  data R = R_Index | R_Foo | R_Bar NumRoute | R_Bar2 NumRoute
    deriving stock (Show, Eq, Generic)
    deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
    deriving (HasSubRoutes, IsRoute) via (GenericRoute R '[WithModel M])
  
  -- We must derive this manually due to ambiguity in field types (Int)
  instance HasSubModels R where
    subModels (a, b, _) =
      I () :* I () :* I a :* I b :* Nil
  
  data NumRoute = NumRoute
    deriving stock (Show, Eq, Generic)
  
  instance IsRoute NumRoute where
    type RouteModel NumRoute = Int
    routePrism n =
      let fp = show n <> ".html"
       in toPrism_ $
            prism' (const fp) $ \s -> do
              guard $ s == fp
              pure NumRoute
    routeUniverse _ = [NumRoute]
  
  instance EmaSite R where
    siteInput _ () = pure $ pure (42, 21, "inner")
    siteOutput _ m r = pure $ Asset.AssetGenerated Asset.Html $ show r <> show m
  
  -- --warnings -c "cabal repl ema -f with-examples" -T Ema.Route.Generic.main  --setup ":set args gen /tmp"
  main :: IO ()
  main = Ema.runSite_ @R ()
  
  -- ---
  -- Let's try defining a top-level route using `R` to see how EmaSite instances compose.
  -- --
  
  type TM = (M, String)
  
  data TR = TR_Index | TR_Inner R
    deriving stock (Show, Eq, Generic)
    deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
    deriving (HasSubRoutes, HasSubModels, IsRoute) via (GenericRoute TR '[WithModel TM])
  
  instance EmaSite TR where
    siteInput x () = do
      m1 <- siteInput @R x ()
      pure $ fmap (,"TOP") m1
    siteOutput rp m = \case
      r@TR_Index ->
        pure $ Asset.AssetGenerated Asset.Html $ show r <> show m
      TR_Inner r ->
        -- Might as well provide a `innerSiteOutput (_As @TR_Inner)`?
        siteOutput @R (rp % _As @"TR_Inner") (trInnerModel m) r
      where
        trInnerModel model =
          let I () :* I m' :* Nil = subModels @TR model
           in m'
  
  mainTop :: IO ()
  mainTop = Ema.runSite_ @TR ()
  
  -- ---
  -- Ensure that the constant model case (simple one) still works
  -- --
  
  type M2 = (Int, String)
  
  data R2 = R2_Index | R2_Foo | R2_Bar BarRoute | R2_Bar2 BarRoute
    deriving stock (Show, Eq, Generic)
    deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
    deriving (HasSubRoutes, HasSubModels, IsRoute) via (GenericRoute R2 '[WithModel M2])
  
  data BarRoute = BarRoute
    deriving stock (Show, Eq, Generic)
    deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
    deriving (HasSubRoutes, HasSubModels, IsRoute) via (GenericRoute BarRoute '[WithModel M2])
  
  instance EmaSite R2 where
    siteInput _ () = pure $ pure (21, "inner")
    siteOutput _ m r = pure $ Asset.AssetGenerated Asset.Html $ show r <> show m
  
  mainConst :: IO ()
  mainConst = Ema.runSite_ @R2 ()
  
  -- ---
  -- Ensure deriveIsRoute works with (partial) subroutes
  -- --
  
  data M3
    = M3
    deriving stock (Eq, Show, Generic)
  
  data R3
    = R3_Sub R3_SubR
    | R3_Index
  
  data R3_SubR
    = R3_SubR
    deriving stock (Show, Eq, Ord, Generic)
    deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
    deriving
      (HasSubRoutes, HasSubModels, IsRoute)
      via ( GenericRoute
              R3_SubR
              '[ WithSubRoutes
                  '[ FileRoute "example.html"
                   ]
               ]
          )
  
  SOP.deriveGeneric ''R3
  deriveIsRoute
    ''R3
    [t|
      [ WithModel M3
      , WithSubRoutes
          [ R3_SubR
          , ()
          ]
      ]
      |]
  
  -- ---
  -- Ensure deriveIsRoute works in a no-subroute case
  -- --
  
  data M4
    = M4
    deriving stock (Eq, Show, Generic)
  
  data R4
    = R4_Index
    | R3_About
  
  SOP.deriveGeneric ''R4
  deriveIsRoute ''R4 [t|'[WithModel M4]|]
  