\import Category
\import Category.Adjoint
\import Category.Comma
\import Category.Functor
\import Category.Limit
\import Equiv
\import Function.Meta
\import HLevel
\import Logic
\import Meta
\import Paths
\import Paths.Meta

\class RightAdjointCounit \extends Functor {
  | LAdj : Functor D C
  | epsilon : NatTrans (Comp LAdj \this) Id
  \field isAdjoint-inv {X : D} {Y : C} : Equiv {Hom X (F Y)} {Hom (LAdj X) Y} (epsilon Y  LAdj.Func __)

  \func eta : NatTrans Id (Comp \this LAdj) \cowith
    | trans X => isAdjoint-inv.ret (id (LAdj X))
    | natural {_} {_} _ => isAdjoint-inv.isInj $ unfold $ rewrite (Func-o {LAdj}, Func-o {LAdj}) $
                                                          rewriteI (o-assoc, o-assoc) $
                                                          rewrite (isAdjoint-inv.f_ret, id-left, epsilon.natural) $
                                                          rewrite (o-assoc, isAdjoint-inv.f_ret, id-right) idp

  \func adjoint-eta  {X : D} {Y : C} (g : Hom (LAdj X) Y) : isAdjoint-inv.ret g = Func g  eta X =>
    isAdjoint-inv.isInj $ unfold $ rewrite (isAdjoint-inv.f_ret _, Func-o {LAdj}) $
                                   rewriteI o-assoc $ rewrite (epsilon.natural, o-assoc, isAdjoint-inv.f_ret _, id-right) idp
} \where {
  \use \coerce fromAdjoint (F : RightAdjoint) : RightAdjointCounit \cowith
    | Functor => F
    | LAdj => F.LAdj
    | epsilon => F.epsilon
    | isAdjoint-inv => \new Equiv {
      | ret => F.isAdjoint
      | ret_f => F.eta_eps_equiv.f_sec
      | sec => F.isAdjoint
      | f_sec => F.eta_eps_equiv.ret_f
    }

  \use \coerce toAdjoint (F : RightAdjointCounit) : RightAdjoint => \new RightAdjoint {
    | Functor => F
    | LAdj => LAdj
    | eta => eta
    | isAdjoint => \new QEquiv {
      | ret => isAdjoint-inv
      | ret_f _ => unfold $ rewriteI (adjoint-eta _ ) $ isAdjoint-inv.f_ret _
      | f_sec _ => isAdjoint-inv.isInj $ unfold $ run {
        rewrite (Func-o {LAdj}),
        rewriteI o-assoc,
        rewrite (epsilon.natural, o-assoc, isAdjoint-inv.f_ret _, id-right) idp
      }
    }
  }
}

\func comma-precat {C D : Precat} (L : Functor D C) (b : C) => commaPrecat {D} {TrivialCat} {C} L (Const b)

\func comma-cat {C : Precat} {D : Cat} (L : Functor D C) (b : C) => commaCat {D} {TrivialCat} {C} L (Const b)

\class Coreflection {C D : Precat} (L : Functor D C) (B : C) {
  | \classifying Coreflected : D
  | corefl-map : Hom (L Coreflected) B
  \property isCoreflection {Z : D} : Equiv {Hom Z Coreflected} {Hom (L Z) B} (\lam x => corefl-map  (L.Func x))

  \func to-comma : comma-precat L B => (Coreflected, (), corefl-map)

  \func coreflection-map (x : comma-precat L B) : Hom {comma-precat L B} x to-comma
    | (x, s, d) => (Equiv.ret {isCoreflection} d, id {TrivialCat} s, rewrite id-left $ Equiv.f_ret {isCoreflection} d)

  \func to-comma-terminal  : terminal-obj (comma-precat L B) => terminal-ind {comma-precat L B} to-comma
      (\lam (p, s, q) => Contr.make (coreflection-map (p, s, q))
          (\lam a => exts (inv $ Equiv.adjoint $ a.3 *> id-left, cases a.2 \with {
            | Graph.empty p1 => idp
            | Graph.cons e x => absurd e
          }))
      )
}
  \where {
    \func from-comma-terminal {C D : Precat} {L : Functor D C} {b : C} (terminal : terminal-obj (comma-precat L b))
      : Coreflection L b => \new Coreflection {
      | Coreflected => terminal.apex.1
      | corefl-map => terminal.apex.3
      | isCoreflection {z} => \new QEquiv {
        | ret g => (terminalMap' {comma-precat L b} {terminal} {z, (), g}).1
        | f_sec y => (terminalMap' {comma-precat L b} {terminal} {z, (), y}).3 *> id-left
        | ret_f x =>
          \let x-comma : comma-precat L b => (z, (), terminal.apex.3  L.Func x)
               | x-map : Hom {comma-precat L b} x-comma terminal.apex => (x, id {TrivialCat} (), inv id-left)
          \in
            path (\lam i => ((inv $ terminal-unique' {comma-precat L b} {terminal} {x-comma} {x-map}) @ i).1)
      }
    }

    \func terminal-in-comma {C D : Precat} (L : Functor D C) (B : C)
      : Section {Coreflection L B} {terminal-obj (comma-precat L B)} => \new Section {
      | f coref => Coreflection.to-comma-terminal {coref}
      | ret => Coreflection.from-comma-terminal
      | ret_f _ => idp
    }
      \where {
        \func to-comma-isInj (x y : Coreflection L B) (eq : Coreflection.to-comma-terminal {x} = Coreflection.to-comma-terminal {y})
          : x = y =>
          inv (ret_f {terminal-in-comma L B} x) *> pmap (ret {terminal-in-comma L B}) eq *> ret_f {terminal-in-comma L B} y
      }

    \use\level isProp {C : Precat} {D : Cat} {L : Functor D C} {B : C}
                      (x y : Coreflection L B) : x = y =>
      \let s : Coreflection.to-comma-terminal {x} = Coreflection.to-comma-terminal {y} => terminal-prop {comma-cat L B} _ _ \in
        terminal-in-comma.to-comma-isInj x y s
  }

\class RightAdjointCoreflection  \extends Functor {
  | L : Functor D C
  \field coreflection (Z : C) : Coreflection {C} {D} L Z
  | F X => coreflection X
  | Func {X} {_} f => Equiv.ret {isCoreflection} (f  corefl-map {coreflection X})
  | Func-id {_} => unfold $ rewrite id-left $ inv $ Equiv.adjoint $ unfold $ rewrite (Func-id {L}, id-right) idp
  | Func-o {_} {Y} {Z} {_} {_} => run {
    unfold,
    inv,
    Equiv.adjoint,
    unfold,
    rewrite (Func-o {L}),
    rewriteI o-assoc ,
    rewrite (Equiv.f_ret {isCoreflection {coreflection Z}}),
    rewrite o-assoc ,
    rewrite (Equiv.f_ret {isCoreflection {coreflection Y}}),
    inv o-assoc
  }

  \func eval-trans : NatTrans (Comp L \this) Id \cowith {
    | trans c => corefl-map {coreflection c}
    | natural _ => unfold $ Equiv.f_ret {isCoreflection} _
  }

  \func is-adjoint-counit {X : D} {Y : C} : Equiv {Hom X (F Y)} {Hom (L X) Y} (eval-trans Y  L.Func __) =>
    isCoreflection
}
  \where {
    \use \coerce toAdjointCounit(F : RightAdjointCoreflection) : RightAdjointCounit
    \cowith {
      | Functor => F
      | LAdj => F.L
      | epsilon => eval-trans
      | isAdjoint-inv => is-adjoint-counit
    }

    \use \coerce fromAdjointCounit (F : RightAdjointCounit) : RightAdjointCoreflection F.C F.D F.LAdj \cowith {
      | coreflection => \lam Z => \new Coreflection {
        | Coreflected => F Z
        | corefl-map => F.epsilon Z
        | isCoreflection => F.isAdjoint-inv
      }}

    \use\level isProp {C : Precat} {D : Cat} (L : Functor D C)
    (adj1 adj2 : RightAdjointCoreflection C D L) : adj1 = adj2
      \elim adj1, adj2
        | (coref1), (coref2) => exts (\lam Z => Coreflection.isProp {C} {D} {L} (coreflection {coref1} Z) (coreflection{coref2} Z))
  }