{-# OPTIONS --safe #-}
module Cubical.Categories.Presheaf.More where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Structure
open import Cubical.Data.Sigma

open import Cubical.Categories.Category hiding (isIso)
open import Cubical.Categories.Limits.Terminal
open import Cubical.Categories.Constructions.Lift
open import Cubical.Categories.Constructions.Elements
open import Cubical.Categories.Instances.Sets
open import Cubical.Categories.Instances.Functors
open import Cubical.Categories.Functor.Base
open import Cubical.Categories.NaturalTransformation
open import Cubical.Categories.Presheaf.Base
open import Cubical.Categories.Presheaf.Representable

open import Cubical.Categories.Instances.Sets.More
open import Cubical.Categories.Isomorphism.More

open Functor

private
  variable
     ℓ' ℓS ℓS' : Level
    ℓD ℓD' : Level

PshIso : (C : Category  ℓ')
         (P : Presheaf C ℓS)
         (Q : Presheaf C ℓS')  Type (ℓ-max (ℓ-max (ℓ-max  ℓ') ℓS) ℓS')
PshIso {ℓS = ℓS}{ℓS' = ℓS'} C P Q =
  CatIso (FUNCTOR (C ^op) (SET (ℓ-max ℓS ℓS')))
    (LiftF { = ℓS}{ℓ' = ℓS'} ∘F P)
    (LiftF { = ℓS'}{ℓ' = ℓS} ∘F Q)

IdPshIso : (C : Category  ℓ') (P : Presheaf C ℓS)  PshIso C P P
IdPshIso C P = idCatIso

𝓟o = Presheaf

𝓟* : Category  ℓ'  (ℓS : Level)  Type (ℓ-max (ℓ-max  ℓ') (ℓ-suc ℓS))
𝓟* C ℓS = Functor C (SET ℓS)

module _ (C : Category  ℓ') (c : C .Category.ob) where
  open Category
  open UniversalElement

  selfUnivElt :  UniversalElement C (C [-, c ])
  selfUnivElt .vertex = c
  selfUnivElt .element = C .id
  selfUnivElt .universal A = isoToIsEquiv (iso _  z  z)
    (C .⋆IdR)
    (C .⋆IdR))

  selfUnivEltᵒᵖ : UniversalElement (C ^op) (C [ c ,-])
  selfUnivEltᵒᵖ .vertex = c
  selfUnivEltᵒᵖ .element = C .id
  selfUnivEltᵒᵖ .universal _ = isoToIsEquiv (iso _  z  z)
    (C .⋆IdL)
    (C .⋆IdL))

module _ {ℓo}{ℓh}{ℓp} (C : Category ℓo ℓh) (P : Presheaf C ℓp) where
  open UniversalElement
  open Category
  UniversalElementOn : C .ob  Type (ℓ-max (ℓ-max ℓo ℓh) ℓp)
  UniversalElementOn vertex =
    Σ[ element  (P  vertex ) .fst ] isUniversal C P vertex element

  UniversalElementToUniversalElementOn :
    (ue : UniversalElement C P)  UniversalElementOn (ue .vertex)
  UniversalElementToUniversalElementOn ue .fst = ue .element
  UniversalElementToUniversalElementOn ue .snd = ue .universal

module PresheafNotation {ℓo}{ℓh}
       {C : Category ℓo ℓh} {ℓp} (P : Presheaf C ℓp)
       where
  private
    module C = Category C
  p[_] : C.ob  Type ℓp
  p[ x ] =  P  x  

  _⋆_ :  {x y} (f : C [ x , y ]) (g : p[ y ])  p[ x ]
  f  g = P .F-hom f g

  ⋆IdL :  {x} (g : p[ x ])  C.id  g  g
  ⋆IdL = funExt⁻ (P .F-id)

  ⋆Assoc :  {x y z} (f : C [ x , y ])(g : C [ y , z ])(h : p[ z ]) 
    (f C.⋆ g)  h  f  (g  h)
  ⋆Assoc f g = funExt⁻ (P .F-seq g f)

  ⟨_⟩⋆⟨_⟩ :  {x y} {f f' : C [ x , y ]} {g g' : p[ y ]}
             f  f'  g  g'  f  g  f'  g'
   f≡f' ⟩⋆⟨ g≡g'  = cong₂ _⋆_ f≡f' g≡g'

  isSetPsh :  {x}  isSet (p[ x ])
  isSetPsh {x} = (P  x ) .snd

-- This should eventually be upstreamed to go in the UniversalElement
-- module itself
module UniversalElementNotation {ℓo}{ℓh}
       {C : Category ℓo ℓh} {ℓp} {P : Presheaf C ℓp}
       (ue : UniversalElement C P)
       where
  open Category
  open UniversalElement ue public
  open NatTrans
  open NatIso
  open Iso
  REPR : Representation C P
  REPR = universalElementToRepresentation C P ue

  unIntroNT : NatTrans (LiftF {ℓ' = ℓp} ∘F (C [-, vertex ]))
                       (LiftF {ℓ' = ℓh} ∘F P)
  unIntroNT = REPR .snd .trans

  introNI : NatIso (LiftF {ℓ' = ℓh} ∘F P) (LiftF {ℓ' = ℓp} ∘F (C [-, vertex ]))
  introNI = symNatIso (REPR .snd)

  universalIso :  (c : C .ob)  Iso (C [ c , vertex ])  P  c  
  universalIso c = equivToIso (_ , universal c)

  private
    module P = PresheafNotation P
    module C = Category C

  intro :  {c}  P.p[ c ]  C [ c , vertex ]
  intro = universalIso _ .inv

  intro⟨_⟩ :  {c}  {f g : P.p[ c ]}  f  g  intro f  intro g
  intro⟨ p  = cong intro p

  opaque
    β :  {c}  {p : P.p[ c ]}  (intro p P.⋆ element)  p
    β = universalIso _ .rightInv _

    η :  {c}  {f : C [ c , vertex ]}  f  intro (f P.⋆ element)
    η {f = f} = sym (universalIso _ .leftInv _)

    weak-η : C .id  intro element
    weak-η = η  cong intro (∘ᴾId C P _)

    extensionality :  {c}  {f f' : C [ c , vertex ]}
                    (f P.⋆ element)  (f' P.⋆ element)
                    f  f'
    extensionality = isoFunInjective (equivToIso (_ , (universal _))) _ _

    intro≡ :  {c}  {p : P.p[ c ]}{f : C [ c , vertex ]}
       p  f P.⋆ element
       intro p  f
    intro≡ p≡f*elt = intro⟨ p≡f*elt   sym η

    intro-natural :  {c' c}  {p : P.p[ c ]}{f : C [ c' , c ]}
                   f C.⋆ intro p  intro (f P.⋆ p)
    intro-natural = extensionality
      ( (∘ᴾAssoc C P _ _ _
       cong (action C P _) β)
       sym β)

-- Natural transformation between presheaves of different levels
module _ {C : Category  ℓ'}(P : Presheaf C ℓS)(Q : Presheaf C ℓS') where
  private
    module C = Category C
    module P = PresheafNotation P
    module Q = PresheafNotation Q
  PshHom : Type _
  PshHom = Σ[ α  (∀ (x : C.ob)  P.p[ x ]  Q.p[ x ]) ]
    (∀ x y (f : C [ x , y ]) (p : P.p[ y ])→
     α x (f P.⋆ p)  (f Q.⋆ α y p))

  isPropN-hom :  (α : (∀ (x : C.ob)  P.p[ x ]  Q.p[ x ])) 
    isProp (∀ x y (f : C [ x , y ]) (p : P.p[ y ])→
     α x (f P.⋆ p)  (f Q.⋆ α y p))
  isPropN-hom = λ _  isPropΠ λ _  isPropΠ λ _  isPropΠ λ _  isPropΠ λ _  Q.isSetPsh _ _

  isSetPshHom : isSet PshHom
  isSetPshHom = isSetΣ (isSetΠ  _  isSet→ Q.isSetPsh)) λ _  isProp→isSet (isPropN-hom _)

module _ {C : Category  ℓ'}{P : Presheaf C ℓS}{Q : Presheaf C ℓS'} where
  makePshHomPath :  {α β : PshHom P Q}  α .fst  β .fst
    α  β
  makePshHomPath = ΣPathPProp (isPropN-hom P Q)