{-# OPTIONS --safe #-}
module Cubical.Categories.Morphism where

open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Prelude
open import Cubical.Data.Sigma
open import Cubical.Categories.Category


private
  variable
     ℓ' : Level

-- C needs to be explicit for these definitions as Agda can't infer it
module _ (C : Category  ℓ') where
  open Category C

  private
    variable
      x y z v w : ob

  isMonic : Hom[ x , y ]  Type (ℓ-max  ℓ')
  isMonic {x} {y} f =
     {z} {a a' : Hom[ z , x ]}  f  a  f  a'  a  a'

  isPropIsMonic : (f : Hom[ x , y ])  isProp (isMonic f)
  isPropIsMonic _ = isPropImplicitΠ  _  (isPropImplicitΠ2
     a a'  (isProp→ (isOfHLevelPath' 1 isSetHom a a')))))

  postcompCreatesMonic : (f : Hom[ x , y ]) (g : Hom[ y , z ])
     isMonic (f  g)  isMonic f
  postcompCreatesMonic f g monic {a = a} {a' = a'} fa≡fa' =
    monic (sym (⋆Assoc a f g)  cong (_⋆ g) fa≡fa'  ⋆Assoc a' f g)

  monicComp : (f : Hom[ x , y ]) (g : Hom[ y , z ])
     isMonic f  isMonic g  isMonic (f  g)
  monicComp f g mon-f mon-g {a = a} {a' = a'} eq =
    mon-f (mon-g (⋆Assoc a f g  eq  sym (⋆Assoc a' f g)))

  monicId : {x : ob}  isMonic (id {x})
  monicId {a = a} {a' = a'} eq = sym (⋆IdR a)  eq  ⋆IdR a'

  retraction⇒monic : (f : Hom[ x , y ]) (lInv : Hom[ y , x ])
     (lInv  f  id)  isMonic f
  retraction⇒monic f lInv eq =
    postcompCreatesMonic f lInv (subst isMonic (sym eq) monicId)

  isEpic : (Hom[ x , y ])  Type (ℓ-max  ℓ')
  isEpic {x} {y} g =
     {z} {b b' : Hom[ y , z ]}  b  g  b'  g  b  b'

  isPropIsEpic : (f : Hom[ x , y ])  isProp (isEpic f)
  isPropIsEpic _ = isPropImplicitΠ  _  (isPropImplicitΠ2
     a a'  (isProp→ (isOfHLevelPath' 1 isSetHom a a')))))

  precompCreatesEpic : (f : Hom[ x , y ]) (g : Hom[ z , x ])
     isEpic (g  f)  isEpic f
  precompCreatesEpic f g epic {b = b} {b' = b'} bf≡b'f =
    epic (⋆Assoc g f b  cong (g ⋆_) bf≡b'f  sym (⋆Assoc g f b'))

  -- A morphism is split monic if it has a *retraction*
  isSplitMon : (Hom[ x , y ])  Type ℓ'
  isSplitMon {x} {y} f = ∃[ r  Hom[ y , x ] ] r  f  id

  -- A morphism is split epic if it has a *section*
  isSplitEpi : (Hom[ x , y ])  Type ℓ'
  isSplitEpi {x} {y} g = ∃[ s  Hom[ y , x ] ] g  s  id

  record areInv (f : Hom[ x , y ]) (g : Hom[ y , x ]) : Type ℓ' where
    field
      sec : g  f  id
      ret : f  g  id


-- C can be implicit here
module _ {C : Category  ℓ'} where
  open Category C

  private
    variable
      x y z v w : ob

  open areInv

  symAreInv : {f : Hom[ x , y ]} {g : Hom[ y , x ]}  areInv C f g  areInv C g f
  sec (symAreInv x) = ret x
  ret (symAreInv x) = sec x

  -- equational reasoning with inverses
  invMoveR :  {f : Hom[ x , y ]} {g : Hom[ y , x ]} {h : Hom[ z , x ]} {k : Hom[ z , y ]}
            areInv C f g
            h  f  k
            h  k  g
  invMoveR {f = f} {g} {h} {k} ai p
    = h
    ≡⟨ sym (⋆IdR _) 
      (h  id)
    ≡⟨ cong (h ⋆_) (sym (ai .ret)) 
      (h  (f  g))
    ≡⟨ sym (⋆Assoc _ _ _) 
      ((h  f)  g)
    ≡⟨ cong (_⋆ g) p 
      k  g
    

  invMoveL :  {f : Hom[ x , y ]} {g : Hom[ y , x ]} {h : Hom[ y , z ]} {k : Hom[ x , z ]}
           areInv C f g
           f  h  k
           h  g  k
  invMoveL {f = f} {g} {h} {k} ai p
    = h
    ≡⟨ sym (⋆IdL _) 
      id  h
    ≡⟨ cong (_⋆ h) (sym (ai .sec)) 
      (g  f)  h
    ≡⟨ ⋆Assoc _ _ _ 
      g  (f  h)
    ≡⟨ cong (g ⋆_) p 
      (g  k)
    

  invFlipSq : {f₁⁻¹ : Hom[ x , y ]} {f₁ : Hom[ y , x ]}
              {f₂⁻¹ : Hom[ v , w ]} {f₂ : Hom[ w , v ]}
              {g : Hom[ x , v ]} {h : Hom[ y , w ]}
             areInv C f₁ f₁⁻¹  areInv C f₂ f₂⁻¹
             h  f₂  f₁  g
             g  f₂⁻¹  f₁⁻¹  h
  invFlipSq {f₁⁻¹ = f₁⁻¹} {f₁} {f₂⁻¹} {f₂} {g} {h} inv₁ inv₂ sq =
    g  f₂⁻¹                ≡⟨ cong (_⋆ f₂⁻¹) (sym (⋆IdL _)) 
    (id  g)  f₂⁻¹         ≡⟨ cong  m  (m  g)  f₂⁻¹) (sym (inv₁ .sec)) 
    ((f₁⁻¹  f₁)  g)  f₂⁻¹ ≡⟨ cong (_⋆ f₂⁻¹) (⋆Assoc _ _ _) 
    (f₁⁻¹  (f₁  g))  f₂⁻¹ ≡⟨ ⋆Assoc _ _ _ 
    f₁⁻¹  ((f₁  g)  f₂⁻¹) ≡⟨ cong  m  f₁⁻¹  (m  f₂⁻¹)) (sym sq) 
    f₁⁻¹  ((h  f₂)  f₂⁻¹) ≡⟨ cong (f₁⁻¹ ⋆_) (⋆Assoc _ _ _) 
    f₁⁻¹  (h  (f₂  f₂⁻¹)) ≡⟨ cong  m  f₁⁻¹  (h  m)) (inv₂ .ret) 
    f₁⁻¹  (h  id)         ≡⟨ cong (f₁⁻¹ ⋆_) (⋆IdR _) 
    f₁⁻¹  h 

  open isIso

  isIso→areInv :  {f : Hom[ x , y ]}
                (isI : isIso C f)
                areInv C f (isI .inv)
  sec (isIso→areInv isI) = sec isI
  ret (isIso→areInv isI) = ret isI


  -- Back and forth between isIso and CatIso

  isIso→CatIso :  {f : C [ x , y ]}
                isIso C f
                CatIso C x y
  isIso→CatIso x = _ , x

  CatIso→isIso : (cIso : CatIso C x y)
                isIso C (cIso .fst)
  CatIso→isIso = snd

  CatIso→areInv : (cIso : CatIso C x y)
                 areInv C (cIso .fst) (cIso .snd .inv)
  CatIso→areInv cIso = isIso→areInv (CatIso→isIso cIso)

  -- reverse of an iso is also an iso
  symCatIso :  {x y}
              CatIso C x y
              CatIso C y x
  symCatIso (mor , isiso inv sec ret) = inv , isiso mor ret sec