{-# OPTIONS --safe --postfix-projections #-}

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function renaming (uncurry to λ⟨,⟩_)
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Transport
open import Cubical.Foundations.CartesianKanOps

open import Cubical.Data.Sigma.Properties


module Cubical.Modalities.Lex
  ( :  {}  Type   Type )
  (η :  {} {A : Type }  A   A)
  (isModal :  {}  Type   Type )
  (let isModalFam = λ { ℓ' : Level} {A : Type } (B : A  Type ℓ')  (x : A)  isModal (B x))
  (idemp :  {} {A : Type }  isModal ( A))
  (≡-modal :  {} {A : Type } {x y : A} (A-mod : isModal A)  isModal (x  y))
  (◯-ind :  { ℓ'} {A : Type } {B :  A  Type ℓ'} (B-mod : isModalFam B) (f : (x : A)  B (η x))  ([x] :  A)  B [x])
  (◯-ind-β :  { ℓ'} {A : Type } {B :  A  Type ℓ'} (B-mod : isModalFam B) (f : (x : A)  B (η x)) (x : A)  ◯-ind B-mod f (η x)  f x)
  (let Type◯ = λ ( : Level)  Σ (Type ) isModal)
  (◯-lex :  {}  isModal (Type◯ ))
  where


private
  variable
      ℓ' : Level

η-at : (A : Type )  A   A
η-at _ = η

module _ where
  private
    variable
      A : Type 
      B : Type ℓ'


  module ◯-rec (B-mod : isModal B) (f : A  B) where
    abstract
      ◯-rec :  A  B
      ◯-rec = ◯-ind  _  B-mod) f

      ◯-rec-β : (x : A)  ◯-rec (η x)  f x
      ◯-rec-β = ◯-ind-β  _  B-mod) f

  open ◯-rec

  module ◯-map (f : A  B) where
    abstract
      ◯-map :  A   B
      ◯-map = ◯-rec idemp λ x  η (f x)

      ◯-map-β : (x : A)  ◯-map (η x)  η (f x)
      ◯-map-β x = ◯-rec-β idemp _ x


open ◯-rec
open ◯-map

module IsModalToUnitIsEquiv (A : Type ) (A-mod : isModal A) where
  abstract
    inv :  A  A
    inv = ◯-rec A-mod λ x  x

    η-retract : retract η inv
    η-retract = ◯-rec-β _ _

    η-section : section η inv
    η-section = ◯-ind  _  ≡-modal idemp) λ x i  η (η-retract x i)

    η-iso : Iso A ( A)
    Iso.fun η-iso = η
    Iso.inv η-iso = inv
    Iso.rightInv η-iso = η-section
    Iso.leftInv η-iso = η-retract

    η-is-equiv : isEquiv (η-at A)
    η-is-equiv = isoToIsEquiv η-iso

abstract
  unit-is-equiv-to-is-modal : {A : Type }  isEquiv (η-at A)  isModal A
  unit-is-equiv-to-is-modal p = transport (cong isModal (sym (ua (η , p)))) idemp

  retract-is-modal
    : {A : Type } {B : Type ℓ'}
     (A-mod : isModal A) (f : A  B) (g : B  A) (r : retract g f)
     isModal B
  retract-is-modal {A = A} {B = B} A-mod f g r =
    unit-is-equiv-to-is-modal (isoToIsEquiv (iso η η-inv η-section η-retract))
    where
      η-inv :  B  B
      η-inv = f  ◯-rec A-mod g

      η-retract : retract η η-inv
      η-retract b = cong f (◯-rec-β A-mod g b)  r b

      η-section : section η η-inv
      η-section = ◯-ind  _  ≡-modal idemp) (cong η  η-retract)


module LiftFam {A : Type } (B : A  Type ℓ') where
  module M = IsModalToUnitIsEquiv (Type◯ ℓ') ◯-lex

  abstract
    ◯-lift-fam :  A  Type◯ ℓ'
    ◯-lift-fam = M.inv  ◯-map  a   (B a) , idemp)

    ⟨◯⟩ :  A  Type ℓ'
    ⟨◯⟩ [a] = ◯-lift-fam [a] .fst

    ⟨◯⟩-modal : isModalFam ⟨◯⟩
    ⟨◯⟩-modal [a] = ◯-lift-fam [a] .snd

    ⟨◯⟩-compute : (x : A)  ⟨◯⟩ (η x)   (B x)
    ⟨◯⟩-compute x =
      ⟨◯⟩ (η x)
        ≡⟨ cong (fst  M.inv) (◯-map-β _ _) 
      M.inv (η ( (B x) , idemp)) .fst
        ≡⟨ cong fst (M.η-retract _) 
       (B x) 



open LiftFam using (⟨◯⟩; ⟨◯⟩-modal; ⟨◯⟩-compute)

module LiftFamExtra {A : Type } {B : A  Type ℓ'} where
  ⟨◯⟩←◯ :  {a}   (B a)  ⟨◯⟩ B (η a)
  ⟨◯⟩←◯ = transport (sym (⟨◯⟩-compute B _))

  ⟨◯⟩→◯ :  {a}  ⟨◯⟩ B (η a)   (B a)
  ⟨◯⟩→◯ = transport (⟨◯⟩-compute B _)

  ⟨η⟩ :  {a}  B a  ⟨◯⟩ B (η a)
  ⟨η⟩ = ⟨◯⟩←◯  η

  abstract
    ⟨◯⟩→◯-section :  {a}  section (⟨◯⟩→◯ {a}) ⟨◯⟩←◯
    ⟨◯⟩→◯-section = transport⁻Transport (sym (⟨◯⟩-compute  _ _))


module Combinators where
  private
    variable
      ℓ'' : Level
      A A′ : Type 
      B : A  Type ℓ'
      C : Σ A B  Type ℓ''

  λ/coe⟨_⟩_ : (p : A  A′)  ((x : A′)  B (coe1→0  i  p i) x))  ((x : A)  B x)
  λ/coe⟨_⟩_ {B = B} p f = coe1→0  i  (x : p i)  B (coei→0  j  p j) i x)) f

open Combinators


module _ {A : Type } {B : A  Type ℓ'} where
  abstract
    Π-modal : isModalFam B  isModal ((x : A)  B x)
    Π-modal B-mod = retract-is-modal idemp η-inv η retr
      where
        η-inv :  ((x : A)  B x)  (x : A)  B x
        η-inv [f] x = ◯-rec (B-mod x)  f  f x) [f]

        retr : retract η η-inv
        retr f = funExt λ x  ◯-rec-β (B-mod x) _ f

    Σ-modal : isModal A  isModalFam B  isModal (Σ A B)
    Σ-modal A-mod B-mod = retract-is-modal idemp η-inv η retr
      where
        h :  (Σ A B)  A
        h = ◯-rec A-mod fst

        h-β : (x : Σ A B)  h (η x)  fst x
        h-β = ◯-rec-β A-mod fst

        f : (i : I) (x : Σ A B)  B (h-β x i)
        f i x = coe1→i  j  B (h-β x j)) i (snd x)

        η-inv :  (Σ A B)  Σ A B
        η-inv y = h y , ◯-ind (B-mod  h) (f i0) y

        retr : (p : Σ A B)  η-inv (η p)  p
        retr p =
          η-inv (η p)
            ≡⟨ ΣPathP (refl , ◯-ind-β _ _ _) 
          h (η p) , f i0 p
            ≡⟨ ΣPathP (h-β _ , λ i  f i p) 
          p 


module Σ-commute {A : Type } (B : A  Type ℓ') where
  open LiftFamExtra

  ◯Σ =  (Σ A B)

  module Σ◯ where
    Σ◯ = Σ ( A) (⟨◯⟩ B)
    abstract
      Σ◯-modal : isModal Σ◯
      Σ◯-modal = Σ-modal idemp (⟨◯⟩-modal _)

  open Σ◯

  η-Σ◯ : Σ A B  Σ◯
  η-Σ◯ (x , y) = η x , ⟨η⟩ y

  module Push where
    abstract
      fun : ◯Σ  Σ◯
      fun = ◯-rec Σ◯-modal η-Σ◯

      compute : fun  η  η-Σ◯
      compute = funExt (◯-rec-β _ _)

  module Unpush where
    abstract
      fun : Σ◯  ◯Σ
      fun =
        λ⟨,⟩ ◯-ind  _  Π-modal λ _  idemp) λ x 
        λ/coe⟨ ⟨◯⟩-compute B x  ◯-map (x ,_)

      compute : fun  η-Σ◯  η
      compute =
        funExt λ p 
        fun (η-Σ◯ p)
          ≡⟨ funExt⁻ (◯-ind-β _ _ _) _ 
        transport refl (◯-map _ _)
          ≡⟨ transportRefl _ 
        ◯-map _ (⟨◯⟩→◯ (⟨η⟩ _))
          ≡⟨ cong (◯-map _) (⟨◯⟩→◯-section _) 
        ◯-map _ (η _)
          ≡⟨ ◯-map-β _ _ 
        η p 


  push-unpush-compute : Push.fun  Unpush.fun  η-Σ◯  η-Σ◯
  push-unpush-compute =
    Push.fun  Unpush.fun  η-Σ◯
      ≡⟨ cong (Push.fun ∘_) Unpush.compute 
    Push.fun  η
      ≡⟨ Push.compute 
    η-Σ◯ 

  unpush-push-compute : Unpush.fun  Push.fun  η  η
  unpush-push-compute =
    Unpush.fun  Push.fun  η
      ≡⟨ cong (Unpush.fun ∘_) Push.compute 
    Unpush.fun  η-Σ◯
      ≡⟨ Unpush.compute 
    η 

  is-section : section Unpush.fun Push.fun
  is-section = ◯-ind  x  ≡-modal idemp) λ x i  unpush-push-compute i x

  is-retract : retract Unpush.fun Push.fun
  is-retract =
    λ⟨,⟩ ◯-ind  _  Π-modal λ _  ≡-modal Σ◯-modal) λ x 
    λ/coe⟨ ⟨◯⟩-compute B x 
     ◯-ind
       _  ≡-modal Σ◯-modal)
       y i  push-unpush-compute i (x , y))

  push-sg-is-equiv : isEquiv Push.fun
  push-sg-is-equiv = isoToIsEquiv (iso Push.fun Unpush.fun is-retract is-section)



isConnected : Type   Type 
isConnected A = isContr ( A)



module FormalDiskBundle {A : Type } where
  𝔻 : A  Type 
  𝔻 a = Σ[ x  A ] η a  η x