\import Category
\import Category.Functor
\import Equiv
\import Function.Meta
\import Meta
\import Paths
\import Paths.Meta

\class RightAdjoint \extends Functor {
  | LAdj : Functor D C
  | eta : NatTrans Id (Comp \this LAdj)
  | epsilon : NatTrans (Comp LAdj \this) Id
  | eta_epsilon-left {Y : C} : Func (epsilon Y)  eta (F Y) = id (F Y)
  | eta_epsilon-right {X : D} : epsilon (LAdj X)  LAdj.Func (eta X) = id (LAdj X)

  \lemma eta_epsilon-equiv {X : D} {Y : C} : QEquiv {Hom (LAdj X) Y} {Hom X (F Y)} (Func __  eta X) (epsilon Y  LAdj.Func __) \cowith
    | ret_f f => pmap (_ ) LAdj.Func-o *> inv o-assoc *> pmap (`∘ _) (epsilon.natural f) *> o-assoc *> pmap (f ) eta_epsilon-right *> id-right
    | f_sec g => pmap (`∘ _) Func-o *> o-assoc *> pmap (_ ) (inv (eta.natural g)) *> inv o-assoc *> pmap (`∘ g) eta_epsilon-left *> id-left

  \lemma eta_Iso {Y : C} (e : Iso (eta (F Y))) : Func (epsilon Y) = e.hinv
    => Iso.hinv-unique (\new SplitMono e.f {
      | hinv => Func (epsilon Y)
      | hinv_f => eta_epsilon-left
    }) e idp
} \where {
    \func rightFactor {C D E : Precat} (G1 : FullyFaithfulFunctor D E) (G2 : Functor E D) (adj : RightAdjoint { | Functor => Comp G1 G2 }) : RightAdjoint \cowith
      | Functor => G2
      | LAdj => Comp adj.LAdj G1
      | eta {
        | trans Y => G1.inverse (adj.eta (G1 Y))
        | natural h => run {
            G1.isFullyFaithful.isInj,
            repeat {2} (rewrite G1.Func-o),
            repeat {2} (rewrite G1.inverse-right),
            adj.eta.natural (G1.Func h)
          }
      }
      | epsilon => adj.epsilon
      | eta_epsilon-left => G1.isFaithful $ G1.Func-o *> pmap (_ ) (G1.isFullyFaithful.f_ret _) *> adj.eta_epsilon-left *> inv G1.Func-id
      | eta_epsilon-right => pmap (_  Func __) (G1.isFullyFaithful.f_ret _) *> adj.eta_epsilon-right
}

\class RightAdjointUnit \extends RightAdjoint {
  \field eta-adjoint {X : D} {Y : C} : Equiv {Hom (LAdj X) Y} {Hom X (F Y)} (Func __  eta X)
  | epsilon {
    | trans X => eta-adjoint.ret (id _)
    | natural f => eta-adjoint.isInj $ unfold $ rewrite (Func-o, Func-o, o-assoc, o-assoc, eta-adjoint.f_ret, id-right, inv (eta.natural _), inv o-assoc, eta-adjoint.f_ret) id-left
  }
  | eta_epsilon-left => eta-adjoint.f_ret _
  | eta_epsilon-right => eta-adjoint.isInj $ unfold $ rewrite (Func-id, Func-o, o-assoc, inv (eta.natural _), inv o-assoc, eta-adjoint.f_ret) idp
}

\class RightAdjointCounit \extends RightAdjoint {
  \field epsilon-adjoint {X : D} {Y : C} : Equiv {Hom X (F Y)} {Hom (LAdj X) Y} (epsilon Y  LAdj.Func __)
  | eta {
    | trans X => epsilon-adjoint.ret (id _)
    | natural f => epsilon-adjoint.isInj $ pmap (_ ) LAdj.Func-o *> inv o-assoc *> pmap (`∘ _) (epsilon-adjoint.f_ret _) *> id-left *>
        inv (pmap (_ ) LAdj.Func-o *> inv o-assoc *> pmap (`∘ _) (epsilon.natural _) *> o-assoc *> pmap (_ ) (epsilon-adjoint.f_ret _) *> id-right)
  }
  | eta_epsilon-left => epsilon-adjoint.isInj $ unfold $ pmap (_ ) LAdj.Func-o *> inv o-assoc *> pmap (`∘ _) (epsilon.natural _) *> o-assoc *> pmap (_ ) (epsilon-adjoint.f_ret _) *> inv (pmap (_ ) LAdj.Func-id)
  | eta_epsilon-right => epsilon-adjoint.f_ret _
}

\class CatEquiv \extends FullyFaithfulFunctor, RightAdjoint {
  \field eta-iso {X : D} : Iso (eta X)
  \field epsilon-iso {Y : C} : Iso (epsilon Y)
  | isFullyFaithful => \new QEquiv {
    | ret g => epsilon _  LAdj.Func g  hinv {epsilon-iso}
    | ret_f f => pmap (`∘ _) (epsilon.natural f) *> o-assoc *> pmap (f ) epsilon-iso.f_hinv *> id-right
    | f_sec g => Func-o *> pmap (`∘ _) (Func-o *> pmap (`∘ _) (eta_Iso eta-iso) *> natural {eta.iso eta-iso} g) *> o-assoc *> pmap (g ) (Iso.hinv-unique (oIso eta-iso (Func-iso epsilon-iso)) idIso eta_epsilon-left) *> id-right
  }

  \protected \func op : CatEquiv \cowith
    | Functor => Functor.op
    | LAdj => LAdj.op
    | eta => NatTrans.op {eta.iso eta-iso}
    | epsilon => NatTrans.op {epsilon.iso epsilon-iso}
    | eta_epsilon-left => Iso.hinv-unique (oIso eta-iso (Func-iso epsilon-iso)) idIso eta_epsilon-left
    | eta_epsilon-right => Iso.hinv-unique (oIso (LAdj.Func-iso eta-iso) epsilon-iso) idIso eta_epsilon-right
    | eta-iso => eta-iso.op.reverse
    | epsilon-iso => epsilon-iso.op.reverse

  \func reverse : CatEquiv \cowith
    | Functor => LAdj
    | LAdj => \this
    | eta => epsilon.iso epsilon-iso
    | epsilon => eta.iso eta-iso
    | eta_epsilon-left => Iso.hinv-unique (oIso (LAdj.Func-iso eta-iso) epsilon-iso) idIso eta_epsilon-right
    | eta_epsilon-right => Iso.hinv-unique (oIso eta-iso (Func-iso epsilon-iso)) idIso eta_epsilon-left
    | eta-iso => epsilon-iso.reverse
    | epsilon-iso => eta-iso.reverse
}