open import Categories.Category.Core
open import Categories.Category.Distributive
open import Categories.Object.NaturalNumbers.Parametrized
open import Monad.Instance.Delay
open import Monad.Instance.Delay.Quotient
open import Categories.Object.Terminal
open import Categories.Functor.Properties using ([_]-resp-Iso)


import Categories.Morphism as M
import Categories.Morphism.Reasoning as MR
import Categories.Morphism.Properties as MP

module Monad.Instance.Delay.Quotient.Epis {o  e} {C : Category o  e} (distributive : Distributive C) (DM : DelayM (Distributive.cocartesian distributive)) (PNNO : ParametrizedNNO C (Distributive.cartesian distributive)) (DQ : DelayQ distributive DM PNNO) where
  open import Categories.Diagram.Coequalizer C

  open Category C
  open HomReasoning
  open Equiv
  open import Category.Distributive.Helper distributive
  open M C
  open MR C
  open MP C
  
  open DelayM DM
  open D-Monad
  open DelayQ DQ
  open import Monad.Instance.Delay.Strong distributive DM
  open D-Strong
  open τ-mod
  open import Algebra.Search cocartesian DM
  open import Algebra.Search.Retraction distributive DM
  open Terminal terminal using ()


  epi-Dρ :  {X}  (s-alg-on : Search-Algebra-on (Ď₀ X))  Epi (D₁ (ρ {X}))
  epi-Dρ {X} s-alg-on f g eq = epi₄ f g (epi₃ (f  D₁ π₁) (g  D₁ π₁) (pullʳ helper  extendʳ eq  pushʳ (sym helper)))
    where
      s-alg : Search-Algebra
      s-alg = record { A = Ď₀ X ; search-algebra-on = s-alg-on }
      open Search-Algebra-on s-alg-on
      helper : D₁ π₁  D₁ (ρ  id)  D₁ ρ  D₁ π₁
      helper = sym D.F.homomorphism  D.F.F-resp-≈ project₁  D.F.homomorphism
      epi₁ :  {Y}  Epi (ρ {X}  id {Y})
      epi₁ {Y} = IsCoequalizer⇒Epi (coeq-productsˡ (id {Y}))
      epi₂ : Epi (τ  (ρ {X}  id {D₀ }))
      epi₂ f g eq = begin 
        f                             ≈⟨ introʳ (Retract.is-retract (tau-retract () s-alg))  
        f  τ   α  D₁ π₁ , D₁ π₂  ≈⟨ extendʳ (epi₁ (f  τ) (g  τ) (assoc  eq  sym-assoc))  
        g  τ   α  D₁ π₁ , D₁ π₂  ≈˘⟨ introʳ (Retract.is-retract (tau-retract () s-alg))  
        g                             
      epi₃ : Epi (D₁ (ρ  id))
      epi₃ f g eq = epi₂ f g (begin 
        f  τ  (ρ  id)    ≈⟨ refl⟩∘⟨ epi₃-helper  
        f  D₁ (ρ  id)  τ ≈⟨ extendʳ eq  
        g  D₁ (ρ  id)  τ ≈˘⟨ refl⟩∘⟨ epi₃-helper  
        g  τ  (ρ  id)    )
        where
        epi₃-helper : τ  (ρ  id)  D₁ (ρ  id)  τ
        epi₃-helper = begin 
          τ  (ρ  id)    ≈⟨ refl⟩∘⟨ ⁂-cong₂ refl (sym D.F.identity)  
          τ  (ρ  D₁ id) ≈⟨ τ-natural ρ id  
          D₁ (ρ  id)  τ 
      epi₄ : Epi (D₁ (π₁ {Ď₀ X} {}))
      epi₄ = Iso⇒Epi ([ D.F ]-resp-Iso (_≅_.iso A×⊤≅A))

  epi-DDρ :  {X}  (s-alg-on : Search-Algebra-on (Ď₀ X))  Epi (D₁ (D₁ (ρ {X})))
  epi-DDρ {X} s-alg-on f g eq = epi₄ f g (epi₃ (f  D₁ (D₁ π₁)) (g  D₁ (D₁ π₁)) (pullʳ helper  extendʳ eq  pushʳ (sym helper)))
        where
          s-alg : Search-Algebra
          s-alg = record { A = Ď₀ X ; search-algebra-on = s-alg-on }
          open Search-Algebra-on s-alg-on
          helper : D₁ (D₁ π₁)  D₁ (D₁ (ρ  id))  D₁ (D₁ ρ)  D₁ (D₁ π₁)
          helper = begin 
            D₁ (D₁ π₁)  D₁ (D₁ (ρ  id)) ≈⟨ sym D.F.homomorphism  
            D₁ (D₁ π₁  D₁ (ρ  id))      ≈⟨ D.F.F-resp-≈ (sym D.F.homomorphism)  
            D₁ (D₁ (π₁  (ρ  id)))       ≈⟨ D.F.F-resp-≈ (D.F.F-resp-≈ project₁)  
            D₁ (D₁ (ρ  π₁))              ≈⟨ D.F.F-resp-≈ D.F.homomorphism  
            D₁ (D₁ ρ  D₁ π₁)             ≈⟨ D.F.homomorphism  
            D₁ (D₁ ρ)  D₁ (D₁ π₁)        
          epi₁ :  {Y}  Epi (ρ {X}  id {Y})
          epi₁ {Y} = IsCoequalizer⇒Epi (coeq-productsˡ (id {Y}))
          epi₂ : Epi (D₁ τ  τ  (ρ {X}  id {D₀ (D₀ (Terminal.⊤ terminal))}))
          epi₂ f g eq = begin 
            f                                                               ≈⟨ introʳ (D.F.F-resp-≈ (Retract.is-retract (tau-retract (Terminal.⊤ terminal) s-alg))  D.F.identity)  
            f  D₁ (τ   α  D₁ π₁ , D₁ π₂ )                              ≈⟨ refl⟩∘⟨ D.F.homomorphism  
            f  D₁ τ  D₁  α  D₁ π₁ , D₁ π₂                              ≈⟨ refl⟩∘⟨ refl⟩∘⟨ insertˡ (Retract.is-retract (tau-retract (D₀ (Terminal.⊤ terminal)) s-alg))  
            f  D₁ τ  τ   α  D₁ π₁ , D₁ π₂   D₁  α  D₁ π₁ , D₁ π₂  ≈⟨ assoc²εβ  ∘-resp-≈ˡ (epi₁ (f  D₁ τ  τ) (g  D₁ τ  τ) (assoc²βε  eq  assoc²εβ))  assoc²βε  
            g  D₁ τ  τ   α  D₁ π₁ , D₁ π₂   D₁  α  D₁ π₁ , D₁ π₂  ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ insertˡ (Retract.is-retract (tau-retract (D₀ (Terminal.⊤ terminal)) s-alg))  
            g  D₁ τ  D₁  α  D₁ π₁ , D₁ π₂                              ≈˘⟨ refl⟩∘⟨ D.F.homomorphism  
            g  D₁ (τ   α  D₁ π₁ , D₁ π₂ )                              ≈˘⟨ introʳ (D.F.F-resp-≈ (Retract.is-retract (tau-retract (Terminal.⊤ terminal) s-alg))  D.F.identity)  
            g                                                               
          epi₃ : Epi (D₁ (D₁ (ρ  id)))
          epi₃ f g eq = epi₂ f g (begin 
            f  D₁ τ  τ  (ρ  id)         ≈⟨ refl⟩∘⟨ epi₃-helper  
            f  D₁ (D₁ (ρ  id))  D₁ τ  τ ≈⟨ extendʳ eq  
            g  D₁ (D₁ (ρ  id))  D₁ τ  τ ≈˘⟨ refl⟩∘⟨ epi₃-helper  
            g  D₁ τ  τ  (ρ  id)         )
            where
            epi₃-helper : D₁ τ  τ  (ρ  id)  D₁ (D₁ (ρ  id))  D₁ τ  τ
            epi₃-helper = begin 
              D₁ τ  τ  (ρ  id)         ≈⟨ refl⟩∘⟨ refl⟩∘⟨ ⁂-cong₂ refl (sym D.F.identity)  
              D₁ τ  τ  (ρ  D₁ id)      ≈⟨ refl⟩∘⟨ τ-natural ρ id  
              D₁ τ  D₁ (ρ  id)  τ      ≈⟨ pullˡ (sym D.F.homomorphism)  
              D₁ (τ  (ρ  id))  τ       ≈⟨ (D.F.F-resp-≈ (∘-resp-≈ʳ (⁂-cong₂ refl (sym D.F.identity)))) ⟩∘⟨refl  
              D₁ (τ  (ρ  D₁ id))  τ    ≈⟨ (D.F.F-resp-≈ (τ-natural ρ id)) ⟩∘⟨refl  
              D₁ (D₁ (ρ  id)  τ)  τ    ≈⟨ pushˡ D.F.homomorphism  
              D₁ (D₁ (ρ  id))  D₁ τ  τ 
          epi₄ : Epi (D₁ (D₁ (π₁ {Ď₀ X} {})))
          epi₄ = Iso⇒Epi ([ D.F ]-resp-Iso ([ D.F ]-resp-Iso (_≅_.iso A×⊤≅A)))