{-
   Strong monads on cartesian categories as extension systems,
   i.e., in terms of unit and bind, deriving the rest of the structure
   https://ncatlab.org/nlab/show/extension+system
-}
{-# OPTIONS --safe --lossy-unification #-}
module Cubical.Categories.Monad.Strength.Cartesian.ExtensionSystem where

open import Cubical.Foundations.Prelude

open import Cubical.Categories.Category
open import Cubical.Categories.Functor renaming (𝟙⟨_⟩ to funcId)
open import Cubical.Categories.Limits.BinProduct.More
open import Cubical.Categories.Limits.Terminal
open import Cubical.Categories.Limits.Terminal.More
open import Cubical.Categories.Limits.Cartesian.Base
open import Cubical.Categories.Comonad.Instances.Environment
open import Cubical.Categories.Monad.ExtensionSystem as Monad

open import Cubical.Tactics.CategorySolver.Reflection

private
  variable
     ℓ' : Level

module _ {C : Category  ℓ'} (bp : BinProducts C) where
  open Category
  open BinProductsNotation bp
  -- open EnvNotation bp

  private
    variable
      Γ Δ a b c : C .ob
      γ δ : C [ Δ , Γ ]
      f g : C [ a , b ]
      -- s t : With Γ [ a , b ]

  -- TODO: reformulate this stuff in terms of Displayed categories.
  --
  -- This is what Jacobs calls the "simple fibration"

--   -- Env [ γ ][ a , b ] is a map Δ × a → b

--   -- With is a fibered category over C. With : C → Cat
--   -- A Strong extension system is a section of this bundle of categories
--   open ExtensionSystemFor
--   record StrongExtensionSystem : Type (ℓ-max ℓ ℓ') where
--     field
--       T : C .ob → C .ob
--       systems : ∀ Γ → Monad.ExtensionSystemFor (With Γ) T
--       -- η ∘ (γ × id) ≡ η
--       η-natural : (γ ^*) ⟪ systems Γ .η {a = a} ⟫ ≡ systems Δ .η
--       -- bind f ∘ (γ × id) ≡ bind (f ∘ (γ × id))
--       bind-natural :
--         (γ ^*) ⟪ systems Γ .bind s ⟫ ≡ systems Δ .bind ((γ ^*) ⟪ s ⟫)
--   -- | TODO: resulting η, bind are natural in all arguments
--   -- If C further has a terminal object we get an "underlying monad"
--   -- on C because Envs 𝟙 ≅ Id
--   module _ (term : Terminal C) (SE : StrongExtensionSystem) where
--     open StrongExtensionSystem SE
--     open TerminalNotation C term
--     open CartesianCategoryNotation (C , term , bp)
--     open isIso
--     -- This follows abstractly from showing (𝟙 ×-) is equivalent to
--     -- the identity monad
--     -- we'll just be explicit here

--     E1 = systems 𝟙
--     -- f ∘ π₂
--     toWith1 : C [ a , b ] → With 𝟙 [ a , b ]
--     toWith1 = C ._⋆_ (unitor-l .fst)

--     -- f ∘ (! , id)
--     fromWith1 : With 𝟙 [ a , b ] → C [ a , b ]
--     fromWith1 = C ._⋆_ (unitor-l .snd .inv)

--     fromTo : fromWith1 (toWith1 f) ≡ f
--     fromTo =
--       sym (C .⋆Assoc _ _ _) ∙
--       cong₂ (comp' C) refl (unitor-l .snd .sec) ∙ C .⋆IdL _

--     toFrom : toWith1 (fromWith1 f) ≡ f
--     toFrom =
--       sym (C .⋆Assoc _ _ _) ∙
--       cong₂ (comp' C) refl (unitor-l .snd .ret) ∙ C .⋆IdL _

--     -- TODO: recover a monad on the original category
--     -- General principle would be that you can transport a monad
--     -- along an equivalence of categories...
--     global-ESF : Monad.ExtensionSystemFor C T
--     global-ESF .η = fromWith1 (E1 .η)
--     global-ESF .bind s = fromWith1 (E1 .bind (toWith1 s))
--     global-ESF .bind-r =
--       cong fromWith1 (cong (E1 .bind) toFrom) ∙
--       cong fromWith1 (E1 .bind-r) ∙ ×β₂
--     global-ESF .bind-l {f = f} =
--       -- (f o π₂)^+ ∘ (!,id) ∘ η ∘ (!, id)
--       -- (f o π₂)^+ ∘ (!,η) ∘ η ∘ (!, id)
--       ((C .⋆Assoc _ _ _) ∙ cong₂ (seq' C) refl
--         (sym (C .⋆Assoc _ _ _) ∙
--         cong₂ (seq' C)
--               (,p-natural ∙ cong₂ _,p_ (𝟙η' {g = π₁}) (C .⋆IdR _)) refl ∙
--               E1 .bind-l {f = (toWith1 f)} ))
--       ∙ sym (C .⋆Assoc _ _ _) ∙ cong₂ (comp' C) refl ×β₂ ∙ C .⋆IdL _
--     -- ((f ∘ π₂)^+ ∘ (! , id)) ∘ ((g ∘ π₂)^+ ∘ (! , id))
--     global-ESF .bind-comp {f = f}{g = g} =
--     -- ((f ∘ π₂)^+ ∘ (! , id)) ∘ ((g ∘ π₂)^+ ∘ (! , id))
--       lem -- f ∘𝟙 g = f^+
--     -- ((f ∘ π₂)^+ ∘ (π₁ , (g ∘ π₂)^+)) ∘ (! , id)
--       ∙ cong₂ (seq' C) refl (cong₂ (comp' C) refl
--         (,p-natural ∙ cong₂ _,p_ (𝟙η' {g = π₁}) (C .⋆IdR _)) ∙ E1 .bind-comp)
--       ∙ cong₂ (seq' C) refl (cong (E1 .bind)
--               -- (E1 .bind (toWith1 f)) ∘ (π₁ , (g ∘ π₂))
--               -- ≡ (E1 .bind (toWith f) ∘ g ∘ π₂)
--               ((cong₂ (comp' C) refl (cong₂ _,p_ 𝟙η' (sym (C .⋆IdR _)) ∙
--               sym ,p-natural) ∙ C .⋆Assoc _ _ _) ∙ C .⋆Assoc _ _ _))
--     -- (((f ∘ π₂)^+ ∘ (! , id) ∘ g) ∘ π₂)^+ ∘ (! , id)
--       where
--         lem : comp' C (fromWith1 (E1 .bind (toWith1 f)))
--               (fromWith1 (E1 .bind (toWith1 g))) ≡
--               ((E1 .bind (toWith1 f)) ∘⟨ C ⟩
--                 ((!t ,p C .id) ∘⟨ C ⟩ E1 .bind (toWith1 g))) ∘⟨ C ⟩
--                   ((!t ,p C .id))
--         lem = solveCat! C
--     StrongMonad→Monad : Monad.ExtensionSystem C
--     StrongMonad→Monad = T , global-ESF

--     -- TODO: once we establish that T is a functor,
--     -- we can show the following is natural
--     σ : C [ Γ × T a , T (Γ × a) ]
--     σ {Γ = Γ} = systems Γ .bind (fromWith1 (E1 .η))
-- module StrongMonadNotation {C : Category ℓ ℓ'}
--   (bp : BinProducts C) (SE : StrongExtensionSystem bp) where
--   open Category
--   open Notation C bp
--   open EnvNotation bp
--   open StrongExtensionSystem SE public
--   private
--     variable
--       Γ Δ a b c : C .ob
--       γ δ : C [ Δ , Γ ]
--       f g : C [ a , b ]
--       s t : With Γ [ a , b ]

--   open Functor

--   PKleisli : C .ob → Category _ _
--   PKleisli Γ = Monad.Kleisli (With Γ) (T , systems Γ)

--   PG : (Γ : C .ob) → Functor (PKleisli Γ) (With Γ)
--   PG Γ = Monad.G ((With Γ)) ((T , systems Γ))

--   bindP : PKleisli Γ [ a , b ] → With Γ [ T a , T b ]
--   bindP {Γ = Γ} = PG Γ .F-hom

--   retP : PKleisli Γ [ a , a ]
--   retP {Γ} = PKleisli Γ .id

--   bindP-comp : bindP f ∘⟨ With Γ ⟩ bindP g ≡ bindP (bindP f ∘⟨ With Γ ⟩ g)
--   bindP-comp {Γ = Γ} = ExtensionSystemFor.bind-comp (systems Γ)

--   bindP-l : bindP f ∘⟨ With Γ ⟩ retP ≡ f
--   bindP-l {Γ = Γ} = ExtensionSystemFor.bind-l (systems Γ)

--   bindP-r : bindP (retP {a = a}) ≡ With Γ .id
--   bindP-r {Γ = Γ} = ExtensionSystemFor.bind-r (systems Γ)

--   open Functor

--   pull : (γ : C [ Δ , Γ ]) → Functor (PKleisli Γ) (PKleisli Δ)
--   pull γ .F-ob = λ z → z
--   pull γ .F-hom f = (γ ^*) ⟪ f ⟫
--   pull γ .F-id = η-natural
--   pull {Δ = Δ} γ .F-seq f g =
--     (γ ^*) .F-seq _ _ ∙ cong₂ (seq' (With Δ)) refl bind-natural