open import Cubical.Modalities.Modality
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Path using (compPathlEquiv; compPathrEquiv)
open import Cubical.Foundations.Structure
open import Cubical.Data.Sigma

module Calf.Computation.Closed where

open import Calf.Core.Abstract
open import Calf.Core.Cost
open import Calf.Value
open import Calf.Value.Closed as  hiding (map; map-∘; join; bind) public
open import Calf.Computation

●ᶜ : 𝒞  𝒞
●ᶜ A .U =  (A .U)
●ᶜ A .is-set = isSet● (A .is-set)
●ᶜ A .charge = ●.map  A .charge
●ᶜ A .charge/0 {a•} = lemma
  where
    opaque
      lemma : ●ᶜ A .charge 0ℂ a•  a•
      lemma =
        ●.elim  a•  ●-≡-isModal (●ᶜ A .charge 0ℂ a•) a•)
           a  cong η• (A .charge/0 {a}))
          a•
●ᶜ A .charge/+ {a•} {c₁} {c₂} = lemma
  where
    opaque
      lemma : ●ᶜ A .charge (c₁ +ℂ c₂) a•  ●ᶜ A .charge c₁ (●ᶜ A .charge c₂ a•)
      lemma =
        ●.elim  a•  ●-≡-isModal (●ᶜ A .charge (c₁ +ℂ c₂) a•) (●ᶜ A .charge c₁ (●ᶜ A .charge c₂ a•)))
           a  cong η• (A .charge/+ {a} {c₁} {c₂}))
          a•

η•ᶜ : A  ●ᶜ A
η•ᶜ .U = η•
η•ᶜ .charge _ _ = refl

isModalᶜ : 𝒞  𝒱
isModalᶜ A = isModal (U A)

𝒞• : 𝒱₁
𝒞• = 𝒞WithStr isModalᶜ

𝒞•-path : {A• B• : 𝒞•}   A• ⟩ᶜ   B• ⟩ᶜ  A•  B•
𝒞•-path p = Σ≡Prop  A  isPropIsEquiv (η•ᶜ {A} .U)) p

isModalᶜ●ᶜ : isModalᶜ (●ᶜ A)
isModalᶜ●ᶜ = isModal●

●ᶜ• : 𝒞  𝒞•
●ᶜ• A = ●ᶜ A , isModalᶜ●ᶜ {A}

U• : 𝒞•  𝒱•
U• A• .fst =  A• ⟩ᶜ .U
U• A• .snd = A• .snd

opaque
  map-charge : (f : A  B) (c : ) (a• : U (●ᶜ A))
     ●.map (f .U) (●ᶜ A .charge c a•)  ●ᶜ B .charge c (●.map (f .U) a•)
  map-charge f c =
    ●.elim  _  ●-≡-isModal _ _)  a  cong η• (f .charge c a))

map : (A  B)  (●ᶜ A  ●ᶜ B)
map f .U = ●.map (f .U)
map f .charge = map-charge f

map-∘ : (f : A  B) (g : B  C)  map f ⨾ᶜ map g  map (f ⨾ᶜ g)
map-∘ f g = ⊸-path refl refl (funExt (●.map-∘ (f .U) (g .U)))

opaque
  map-id : map (idᶜ {A})  idᶜ
  map-id {A} =
    ⊸-path refl refl
      (funExt (●.ind-prop _  _  ●.isSet● (A .is-set) _ _)  _  refl)  _  refl)))

opaque
  map-id-equiv : isEquiv (map (idᶜ {A}) .U)
  map-id-equiv {A} = subst isEquiv (cong  h  h .U) (sym (map-id {A}))) (idIsEquiv _)

opaque
  map-open :  ABS   (f g : A  B)  map f  map g
  map-open {A} {B} p f g =
    ⊸-path
      {A₀ = ●ᶜ A}
      {A₁ = ●ᶜ A}
      {B₀ = ●ᶜ B}
      {B₁ = ●ᶜ B}
      refl
      refl
      (funExt λ a• 
        ◯-isProp● p
          (map {A = A} {B = B} f .U a•)
          (map {A = A} {B = B} g .U a•))

opaque
  join-charge : (c : ) (a•• : U (●ᶜ (●ᶜ A)))
     ●.join (●ᶜ (●ᶜ A) .charge c a••)  ●ᶜ A .charge c (●.join a••)
  join-charge c =
    ●.elim  _  ●-≡-isModal _ _)  _  refl)

join : ●ᶜ (●ᶜ A)  ●ᶜ A
join .U = ●.join
join {A} .charge = join-charge {A}

bind : (A  ●ᶜ B)  (●ᶜ A  ●ᶜ B)
bind k = map k ⨾ᶜ join

opaque
  bind-map : (k : A  ●ᶜ B) (f : B  C)  bind k ⨾ᶜ map f  bind (k ⨾ᶜ map f)
  bind-map k f =
    ⊸-path refl refl
      (funExt (●.elim  _  ●-≡-isModal _ _)  _  refl)))

opaque
  bind-η• : (f : A  B)  bind (f ⨾ᶜ η•ᶜ)  map f
  bind-η• f =
    ⊸-path refl refl
      (funExt (●.elim  _  ●-≡-isModal _ _)  _  refl)))

opaque
  ●ᶜ-rec-charge : (B• : 𝒞•) (g : A   B• ⟩ᶜ) (c : ) (a• : U (●ᶜ A))
     ●.elim  _  strᶜ B•) (g .U) (●ᶜ A .charge c a•)
      B• ⟩ᶜ .charge c (●.elim  _  strᶜ B•) (g .U) a•)
  ●ᶜ-rec-charge B• g c = ●.elim  a•  ●.isModal≡ (strᶜ B•)) (g .charge c)

●ᶜ-rec : (B• : 𝒞•)  (A   B• ⟩ᶜ)  (●ᶜ A   B• ⟩ᶜ)
●ᶜ-rec B• g .U = ●.elim  _  strᶜ B•) (g .U)
●ᶜ-rec B• g .charge = ●ᶜ-rec-charge B• g

●ᶜ-rec-β : (B• : 𝒞•) (g : A   B• ⟩ᶜ) (a : U A)
   ●ᶜ-rec B• g .U (η• a)  g .U a
●ᶜ-rec-β B• g a = refl

opaque
  ⊸-precomp-η•ᶜ-isEquiv : {A : 𝒞} (B• : 𝒞•)
     isEquiv  (f : ●ᶜ A   B• ⟩ᶜ)  η•ᶜ ⨾ᶜ f)
  ⊸-precomp-η•ᶜ-isEquiv B• =
    isoToIsEquiv (iso (η•ᶜ ⨾ᶜ_) (●ᶜ-rec B•)
       g  ⊸-path refl refl refl)
       f  ⊸-path refl refl
        (funExt (●.elim  a•  ●.isModal≡ (strᶜ B•))  a  refl)))))

⊸-precomp-η•ᶜ-≃ : {A : 𝒞} (B• : 𝒞•)  (●ᶜ A   B• ⟩ᶜ)  (A   B• ⟩ᶜ)
⊸-precomp-η•ᶜ-≃ B• = (η•ᶜ ⨾ᶜ_) , ⊸-precomp-η•ᶜ-isEquiv B•

●ᶜ-map-CHARGE
  : (c : ) (a• : U (●ᶜ A))
   map (CHARGE {A = A} c) .U a•  ●ᶜ A .charge c a•
●ᶜ-map-CHARGE c =
  ●.elim  _  ●-≡-isModal _ _)  _  refl)

module _ {A B C : 𝒞} where
  open import Calf.Computation.Pullback

  lex : (f : A  C) (g : B  C)  ●ᶜ (Pullback f g)  Pullback (map f) (map g)
  lex f g = conservativity fwd (equivIsEquiv e)
    where
      e : U (●ᶜ (Pullback f g))  U (Pullback (map f) (map g))
      e = ●.●-pullback

      isProp-at :  ABS   isProp (U (Pullback (map f) (map g)))
      isProp-at abs =
        isPropΣ (◯-isProp● abs) λ _ 
        isPropΣ (◯-isProp● abs) λ _ 
        isProp→isSet (◯-isProp● abs) _ _

      fwd-charge : (c : ) (a• : U (●ᶜ (Pullback f g)))
         equivFun e (●ᶜ (Pullback f g) .charge c a•)
         Pullback (map f) (map g) .charge c (equivFun e a•)
      fwd-charge c =
        ind-prop _  _  Pullback (map f) (map g) .is-set _ _)
           t  ΣPathP
            ( ●.●-pullback-β₁ (Pullback f g .charge c t)
               sym (cong (●ᶜ A .charge c) (●.●-pullback-β₁ t))
            , ΣPathP
              ( ●.●-pullback-β₂ (Pullback f g .charge c t)
                 sym (cong (●ᶜ B .charge c) (●.●-pullback-β₂ t))
              , isProp→PathP  i  ●ᶜ C .is-set _ _) _ _)))
           abs  isProp-at abs _ _)

      fwd : ●ᶜ (Pullback f g)  Pullback (map f) (map g)
      fwd .U = equivFun e
      fwd .charge = fwd-charge

module _ where
  open import Calf.Computation.Copower

  private
    embed :  {X A}  Σᶜ X A .U  Σᶜ X (●ᶜ  A) .U
    embed (x , a) = x , η• a

  Σᶜ-●ᶜ-fwd :  {X A}  ●ᶜ (Σᶜ X A)  ●ᶜ (Σᶜ X (●ᶜ  A))
  Σᶜ-●ᶜ-fwd {X} {A} .U = ●.map (embed {X} {A})
  Σᶜ-●ᶜ-fwd .charge c =
    ●.elim  _  ●-≡-isModal _ _) λ _  refl

  Σᶜ-●ᶜ-fwd-equiv :  {X A}  isEquivᶜ (Σᶜ-●ᶜ-fwd {X} {A})
  Σᶜ-●ᶜ-fwd-equiv {X} {A} =
    subst isEquiv (funExt⁻ ●.map′≡map (embed {X} {A})) (invEquiv ●Σ●≃●Σ .snd)

  Σᶜ-●ᶜ :  {X A}  ●ᶜ (Σᶜ X A)  ●ᶜ (Σᶜ X (●ᶜ  A))
  Σᶜ-●ᶜ {X} {A} =
    conservativity (Σᶜ-●ᶜ-fwd {X} {A}) (Σᶜ-●ᶜ-fwd-equiv {X} {A})