import Mathlib

set_option autoImplicit false

universe u

open CategoryTheory CategoryTheory.Limits AlgebraicGeometry Opposite

namespace AlgebraicGeometry

namespace DescentAction

variable {S S' : Scheme.{u}} (s : S' ⟶ S) {X' : Scheme.{u}} (x' : X' ⟶ S')

noncomputable def unitMap : X' ⟶ pullback (x' ≫ s) s :=
  pullback.lift (𝟙 X') x' (by rw [Category.id_comp])

noncomputable def proj₁₃ :
    pullback (pullback.snd (x' ≫ s) s ≫ s) s ⟶ pullback (x' ≫ s) s :=
  pullback.lift (pullback.fst _ _ ≫ pullback.fst _ _) (pullback.snd _ _)
    (by rw [Category.assoc, pullback.condition]; exact pullback.condition)

@[reassoc (attr := simp)] theorem unitMap_fst : unitMap s x' ≫ pullback.fst _ _ = 𝟙 X' := pullback.lift_fst _ _ _
@[reassoc (attr := simp)] theorem unitMap_snd : unitMap s x' ≫ pullback.snd _ _ = x' := pullback.lift_snd _ _ _
@[reassoc (attr := simp)] theorem proj₁₃_fst :
    proj₁₃ s x' ≫ pullback.fst _ _ = pullback.fst _ _ ≫ pullback.fst _ _ := pullback.lift_fst _ _ _
@[reassoc (attr := simp)] theorem proj₁₃_snd : proj₁₃ s x' ≫ pullback.snd _ _ = pullback.snd _ _ :=
  pullback.lift_snd _ _ _

noncomputable def toTriple : pullback (x' ≫ s) s ⟶ pullback (pullback.snd (x' ≫ s) s ≫ s) s :=
  pullback.lift (𝟙 _) (pullback.fst _ _ ≫ x')
    (by rw [Category.id_comp, Category.assoc]; exact pullback.condition.symm)

@[reassoc (attr := simp)] theorem toTriple_fst : toTriple s x' ≫ pullback.fst _ _ = 𝟙 _ := pullback.lift_fst _ _ _
@[reassoc (attr := simp)] theorem toTriple_snd : toTriple s x' ≫ pullback.snd _ _ = pullback.fst _ _ ≫ x' :=
  pullback.lift_snd _ _ _

theorem toTriple_proj₁₃ : toTriple s x' ≫ proj₁₃ s x' = pullback.fst _ _ ≫ unitMap s x' := by
  apply pullback.hom_ext
  · rw [Category.assoc, proj₁₃_fst, toTriple_fst_assoc, Category.assoc, unitMap_fst, Category.comp_id]
  · rw [Category.assoc, proj₁₃_snd, toTriple_snd, Category.assoc, unitMap_snd]

variable {s x'} in

noncomputable def actMap (act : pullback (x' ≫ s) s ⟶ X') (h : act ≫ x' = pullback.snd _ _) :
    pullback (pullback.snd (x' ≫ s) s ≫ s) s ⟶ pullback (x' ≫ s) s :=
  pullback.lift (pullback.fst _ _ ≫ act) (pullback.snd _ _)
    (by rw [Category.assoc, reassoc_of% h]; exact pullback.condition)

variable {s x'} in
@[reassoc (attr := simp)] theorem actMap_fst (act : pullback (x' ≫ s) s ⟶ X') (h : act ≫ x' = pullback.snd _ _) :
    actMap act h ≫ pullback.fst _ _ = pullback.fst _ _ ≫ act := pullback.lift_fst _ _ _
variable {s x'} in
@[reassoc (attr := simp)] theorem actMap_snd (act : pullback (x' ≫ s) s ⟶ X') (h : act ≫ x' = pullback.snd _ _) :
    actMap act h ≫ pullback.snd _ _ = pullback.snd _ _ := pullback.lift_snd _ _ _

noncomputable def flipMap {X : Scheme.{u}} (f : X ⟶ S) :
    pullback (pullback.snd f s ≫ s) s ⟶ pullback f s :=
  pullback.lift (pullback.fst _ _ ≫ pullback.fst f s) (pullback.snd _ _)
    (by rw [Category.assoc, pullback.condition]; exact pullback.condition)

@[reassoc (attr := simp)] theorem flipMap_fst {X : Scheme.{u}} (f : X ⟶ S) :
    flipMap s f ≫ pullback.fst f s = pullback.fst _ _ ≫ pullback.fst f s := pullback.lift_fst _ _ _
@[reassoc (attr := simp)] theorem flipMap_snd {X : Scheme.{u}} (f : X ⟶ S) :
    flipMap s f ≫ pullback.snd f s = pullback.snd _ _ := pullback.lift_snd _ _ _

end DescentAction

structure DescentAction {S S' : Scheme.{u}} (s : S' ⟶ S) {X' : Scheme.{u}} (x' : X' ⟶ S') where

  act : pullback (x' ≫ s) s ⟶ X'
  act_comp : act ≫ x' = pullback.snd _ _
  unit_act : DescentAction.unitMap s x' ≫ act = 𝟙 X'
  trans : DescentAction.actMap act act_comp ≫ act = DescentAction.proj₁₃ s x' ≫ act

namespace DescentAction

variable {S S' : Scheme.{u}} (s : S' ⟶ S)

noncomputable def canonical {X : Scheme.{u}} (f : X ⟶ S) : DescentAction s (pullback.snd f s) where
  act := flipMap s f
  act_comp := flipMap_snd s f
  unit_act := by
    apply pullback.hom_ext
    · rw [Category.assoc, flipMap_fst, unitMap_fst_assoc, Category.id_comp]
    · rw [Category.assoc, flipMap_snd, unitMap_snd, Category.id_comp]
  trans := by
    apply pullback.hom_ext
    · rw [Category.assoc, flipMap_fst, actMap_fst_assoc, flipMap_fst, Category.assoc, flipMap_fst,
        proj₁₃_fst_assoc]
    · rw [Category.assoc, flipMap_snd, actMap_snd, Category.assoc, flipMap_snd, proj₁₃_snd]

variable {s} in

def Effective {X' : Scheme.{u}} {x' : X' ⟶ S'} (A : DescentAction s x') : Prop :=
  ∃ (X : Scheme.{u}) (f : X ⟶ S) (e : pullback f s ≅ X') (he : e.hom ≫ x' = pullback.snd f s),
    pullback.map (pullback.snd f s ≫ s) s (x' ≫ s) s e.hom (𝟙 S') (𝟙 S)
        (by rw [Category.comp_id, ← Category.assoc, he]) (by rw [Category.comp_id, Category.id_comp]) ≫ A.act =
      (canonical s f).act ≫ e.hom

end DescentAction

namespace DescentAction

variable {S S' : Scheme.{u}} {s : S' ⟶ S} {X' : Scheme.{u}} {x' : X' ⟶ S'} (A : DescentAction s x')

noncomputable def invol : pullback (x' ≫ s) s ⟶ pullback (x' ≫ s) s :=
  pullback.lift A.act (pullback.fst _ _ ≫ x')
    (by rw [reassoc_of% A.act_comp, Category.assoc]; exact pullback.condition.symm)

@[reassoc (attr := simp)] theorem invol_fst : A.invol ≫ pullback.fst _ _ = A.act := pullback.lift_fst _ _ _
@[reassoc (attr := simp)] theorem invol_snd : A.invol ≫ pullback.snd _ _ = pullback.fst _ _ ≫ x' :=
  pullback.lift_snd _ _ _

theorem toTriple_actMap : toTriple s x' ≫ actMap A.act A.act_comp = A.invol := by
  apply pullback.hom_ext
  · rw [Category.assoc, actMap_fst, toTriple_fst_assoc, invol_fst]
  · rw [Category.assoc, actMap_snd, toTriple_snd, invol_snd]

@[reassoc (attr := simp)] theorem invol_act : A.invol ≫ A.act = pullback.fst _ _ := by
  rw [← toTriple_actMap, Category.assoc, A.trans, ← Category.assoc, toTriple_proj₁₃, Category.assoc, A.unit_act,
    Category.comp_id]

@[reassoc (attr := simp)] theorem invol_invol : A.invol ≫ A.invol = 𝟙 _ := by
  apply pullback.hom_ext
  · rw [Category.assoc, invol_fst, invol_act, Category.id_comp]
  · rw [Category.assoc, invol_snd, invol_fst_assoc, A.act_comp, Category.id_comp]

noncomputable def involIso : pullback (x' ≫ s) s ≅ pullback (x' ≫ s) s :=
  ⟨A.invol, A.invol, A.invol_invol, A.invol_invol⟩

theorem act_eq_invol_fst : A.act = A.involIso.hom ≫ pullback.fst _ _ := (A.invol_fst).symm

theorem hom_ext {T : Scheme.{u}} {a b : T ⟶ pullback (x' ≫ s) s} (h₁ : a ≫ pullback.fst _ _ = b ≫ pullback.fst _ _)
    (h₂ : a ≫ A.act = b ≫ A.act) : a = b := by
  apply pullback.hom_ext h₁
  rw [← A.act_comp, reassoc_of% h₂]

theorem equivalence (T : Scheme.{u}) :
    _root_.Equivalence fun x y : T ⟶ X' => ∃ φ : T ⟶ pullback (x' ≫ s) s, φ ≫ pullback.fst _ _ = x ∧ φ ≫ A.act = y where
  refl x := ⟨x ≫ unitMap s x', by rw [Category.assoc, unitMap_fst, Category.comp_id],
    by rw [Category.assoc, A.unit_act, Category.comp_id]⟩
  symm := by
    rintro x y ⟨φ, hφ, hφ'⟩
    exact ⟨φ ≫ A.invol, by rw [Category.assoc, invol_fst, hφ'], by rw [Category.assoc, invol_act, hφ]⟩
  trans := by
    rintro x y z ⟨φ, hφ, hφ'⟩ ⟨ψ, hψ, hψ'⟩
    have w : φ ≫ pullback.snd (x' ≫ s) s ≫ s = (ψ ≫ pullback.snd (x' ≫ s) s) ≫ s := by
      rw [Category.assoc, ← pullback.condition, reassoc_of% hψ, ← reassoc_of% hφ', reassoc_of% A.act_comp,
        pullback.condition]
    let k : T ⟶ pullback (pullback.snd (x' ≫ s) s ≫ s) s := pullback.lift φ (ψ ≫ pullback.snd _ _) w
    have hk₁ : k ≫ actMap A.act A.act_comp = ψ := by
      apply pullback.hom_ext
      · rw [Category.assoc, actMap_fst, pullback.lift_fst_assoc, hφ', hψ]
      · rw [Category.assoc, actMap_snd, pullback.lift_snd]
    refine ⟨k ≫ proj₁₃ s x', by rw [Category.assoc, proj₁₃_fst, pullback.lift_fst_assoc, hφ], ?_⟩
    rw [Category.assoc, ← A.trans, ← Category.assoc, hk₁, hψ']

end DescentAction

namespace DescentAction

section repAct

variable {S S' : Scheme.{u}} (s : S' ⟶ S) (G : (Over S)ᵒᵖ ⥤ Type (u + 1)) {X' : Scheme.{u}} (x' : X' ⟶ S')
  (hX' : ((Over.map s).op ⋙ G).RepresentableBy (Over.mk x'))

noncomputable def fstOver : (Over.map s).obj (Over.mk (pullback.snd (x' ≫ s) s)) ⟶ (Over.map s).obj (Over.mk x') :=
  Over.homMk (pullback.fst (x' ≫ s) s) pullback.condition

noncomputable def univ : G.obj (op ((Over.map s).obj (Over.mk x'))) := hX'.homEquiv (𝟙 (Over.mk x'))

noncomputable def repActOver : Over.mk (pullback.snd (x' ≫ s) s) ⟶ Over.mk x' :=
  hX'.homEquiv.symm (G.map (fstOver s x').op (univ s G x' hX'))

noncomputable def repAct : pullback (x' ≫ s) s ⟶ X' := (repActOver s G x' hX').left

theorem repAct_comp : repAct s G x' hX' ≫ x' = pullback.snd _ _ := Over.w _

theorem map_repActOver_univ :
    G.map ((Over.map s).map (repActOver s G x' hX')).op (univ s G x' hX') =
      G.map (fstOver s x').op (univ s G x' hX') := by
  have h := hX'.homEquiv_comp (repActOver s G x' hX') (𝟙 _)
  rw [Category.comp_id] at h
  have h' : hX'.homEquiv (repActOver s G x' hX') = G.map (fstOver s x').op (univ s G x' hX') := by
    rw [repActOver, Equiv.apply_symm_apply]
  exact h.symm.trans h'

theorem homEquiv_eq_map_univ {U : Over S'} (g : U ⟶ Over.mk x') :
    hX'.homEquiv g = G.map ((Over.map s).map g).op (univ s G x' hX') := by
  have h := hX'.homEquiv_comp g (𝟙 _)
  rw [Category.comp_id] at h
  exact h

theorem unitMap_repAct : unitMap s x' ≫ repAct s G x' hX' = 𝟙 X' := by
  let υ : Over.mk x' ⟶ Over.mk (pullback.snd (x' ≫ s) s) := Over.homMk (unitMap s x') (unitMap_snd s x')
  have hυ : (Over.map s).map υ ≫ fstOver s x' = 𝟙 _ := by
    ext
    change unitMap s x' ≫ pullback.fst _ _ = 𝟙 _
    exact unitMap_fst s x'
  have key : υ ≫ repActOver s G x' hX' = 𝟙 _ := by
    apply hX'.homEquiv.injective
    rw [hX'.homEquiv_comp, repActOver, Equiv.apply_symm_apply]
    change G.map ((Over.map s).map υ).op (G.map (fstOver s x').op (univ s G x' hX')) = univ s G x' hX'
    rw [← types_comp_apply (G.map _) (G.map _), ← G.map_comp, ← op_comp, hυ, op_id, G.map_id, types_id_apply]
  exact congrArg (fun k => k.left) key

theorem actMap_repAct :
    actMap (repAct s G x' hX') (repAct_comp s G x' hX') ≫ repAct s G x' hX' =
      proj₁₃ s x' ≫ repAct s G x' hX' := by

  let α : Over.mk (pullback.snd (pullback.snd (x' ≫ s) s ≫ s) s) ⟶ Over.mk (pullback.snd (x' ≫ s) s) :=
    Over.homMk (actMap (repAct s G x' hX') (repAct_comp s G x' hX')) (actMap_snd _ (repAct_comp s G x' hX'))
  let π : Over.mk (pullback.snd (pullback.snd (x' ≫ s) s ≫ s) s) ⟶ Over.mk (pullback.snd (x' ≫ s) s) :=
    Over.homMk (proj₁₃ s x') (proj₁₃_snd s x')

  let f₁ : (Over.map s).obj (Over.mk (pullback.snd (pullback.snd (x' ≫ s) s ≫ s) s)) ⟶
      (Over.map s).obj (Over.mk (pullback.snd (x' ≫ s) s)) :=
    Over.homMk (pullback.fst _ _) pullback.condition
  have hα : (Over.map s).map α ≫ fstOver s x' = f₁ ≫ (Over.map s).map (repActOver s G x' hX') := by
    ext
    change actMap (repAct s G x' hX') (repAct_comp s G x' hX') ≫ pullback.fst _ _ =
      pullback.fst _ _ ≫ repAct s G x' hX'
    exact actMap_fst _ (repAct_comp s G x' hX')
  have hπ : (Over.map s).map π ≫ fstOver s x' = f₁ ≫ fstOver s x' := by
    ext
    change proj₁₃ s x' ≫ pullback.fst _ _ = pullback.fst _ _ ≫ pullback.fst _ _
    exact proj₁₃_fst s x'
  have key : α ≫ repActOver s G x' hX' = π ≫ repActOver s G x' hX' := by
    apply hX'.homEquiv.injective
    rw [hX'.homEquiv_comp, hX'.homEquiv_comp, repActOver, Equiv.apply_symm_apply]
    change G.map ((Over.map s).map α).op (G.map (fstOver s x').op (univ s G x' hX')) =
      G.map ((Over.map s).map π).op (G.map (fstOver s x').op (univ s G x' hX'))
    rw [← types_comp_apply (G.map _) (G.map _), ← G.map_comp, ← op_comp, hα,
      ← types_comp_apply (G.map (fstOver s x').op) (G.map _), ← G.map_comp, ← op_comp, hπ,
      op_comp, op_comp, G.map_comp, G.map_comp, types_comp_apply, types_comp_apply]
    erw [map_repActOver_univ]
  exact congrArg (fun k => k.left) key

noncomputable def ofRepresentableBy : DescentAction s x' where
  act := repAct s G x' hX'
  act_comp := repAct_comp s G x' hX'
  unit_act := unitMap_repAct s G x' hX'
  trans := actMap_repAct s G x' hX'

end repAct

end DescentAction

namespace DescentAction

section rep

variable {S S' : Scheme.{u}} (s : S' ⟶ S) (G : (Over S)ᵒᵖ ⥤ Type (u + 1)) {X' : Scheme.{u}} (x' : X' ⟶ S')
  (hX' : ((Over.map s).op ⋙ G).RepresentableBy (Over.mk x'))
  {X : Scheme.{u}} (f : X ⟶ S) (e : pullback f s ≅ X') (he : e.hom ≫ x' = pullback.snd f s)

noncomputable def cover : (Over.map s).obj (Over.mk (pullback.snd f s)) ⟶ Over.mk f :=
  Over.homMk (pullback.fst f s) pullback.condition

theorem counit_app_eq_cover : (Over.mapPullbackAdj s).counit.app (Over.mk f) = cover s f := by
  ext
  simp [cover]

noncomputable def eOver : Over.mk (pullback.snd f s) ≅ Over.mk x' := Over.isoMk e he

noncomputable def univBC : G.obj (op ((Over.map s).obj (Over.mk (pullback.snd f s)))) :=
  G.map ((Over.map s).map (eOver s x' f e he).hom).op (univ s G x' hX')

noncomputable def eRel : pullback (pullback.snd f s ≫ s) s ⟶ pullback (x' ≫ s) s :=
  pullback.map _ _ _ _ e.hom (𝟙 _) (𝟙 _) (by rw [Category.comp_id, ← Category.assoc, he])
    (by rw [Category.comp_id, Category.id_comp])

@[reassoc (attr := simp)] theorem eRel_fst : eRel s x' f e he ≫ pullback.fst _ _ = pullback.fst _ _ ≫ e.hom :=
  pullback.lift_fst _ _ _
@[reassoc (attr := simp)] theorem eRel_snd : eRel s x' f e he ≫ pullback.snd _ _ = pullback.snd _ _ :=
  (pullback.lift_snd _ _ _).trans (Category.comp_id _)

theorem univBC_compatible
    (hcompat : eRel s x' f e he ≫ repAct s G x' hX' = (canonical s f).act ≫ e.hom)
    {Z : Over S} (p₁ p₂ : Z ⟶ (Over.map s).obj (Over.mk (pullback.snd f s)))
    (hp : p₁ ≫ cover s f = p₂ ≫ cover s f) :
    G.map p₁.op (univBC s G x' hX' f e he) = G.map p₂.op (univBC s G x' hX' f e he) := by

  let XS : Over S := (Over.map s).obj (Over.mk (pullback.snd f s))
  let ES : Over S := (Over.map s).obj (Over.mk (pullback.snd (pullback.snd f s ≫ s) s))
  let ES' : Over S := (Over.map s).obj (Over.mk (pullback.snd (x' ≫ s) s))
  have hp' : p₁.left ≫ pullback.fst f s = p₂.left ≫ pullback.fst f s := congrArg (fun k => k.left) hp

  let r : Z.left ⟶ pullback (pullback.snd f s ≫ s) s :=
    pullback.lift p₁.left (p₂.left ≫ pullback.snd f s)
      ((Over.w p₁).trans ((Over.w p₂).symm.trans (Category.assoc _ _ _).symm))
  let rS : Z ⟶ ES := Over.homMk r (by
    change r ≫ pullback.snd _ _ ≫ s = Z.hom
    rw [pullback.lift_snd_assoc, Category.assoc]
    exact Over.w p₂)
  let fstS : ES ⟶ XS := Over.homMk (pullback.fst _ _) pullback.condition
  let flipS : ES ⟶ XS := Over.homMk (flipMap s f) (by
    change flipMap s f ≫ pullback.snd f s ≫ s = pullback.snd _ _ ≫ s
    rw [flipMap_snd_assoc])
  have h₁ : p₁ = rS ≫ fstS := by
    ext
    exact (pullback.lift_fst _ _ _).symm
  have h₂ : p₂ = rS ≫ flipS := by
    ext
    change p₂.left = r ≫ flipMap s f
    apply pullback.hom_ext
    · rw [Category.assoc, flipMap_fst, pullback.lift_fst_assoc]
      exact hp'.symm
    · rw [Category.assoc, flipMap_snd]
      exact (pullback.lift_snd _ _ _).symm

  let eS : XS ⟶ (Over.map s).obj (Over.mk x') := (Over.map s).map (eOver s x' f e he).hom
  let eRelS : ES ⟶ ES' := Over.homMk (eRel s x' f e he) (by
    change eRel s x' f e he ≫ pullback.snd _ _ ≫ s = pullback.snd _ _ ≫ s
    rw [eRel_snd_assoc])
  have h₃ : fstS ≫ eS = eRelS ≫ fstOver s x' := by
    ext
    change pullback.fst _ _ ≫ e.hom = eRel s x' f e he ≫ pullback.fst _ _
    exact (eRel_fst s x' f e he).symm
  have h₄ : flipS ≫ eS = eRelS ≫ (Over.map s).map (repActOver s G x' hX') := by
    ext
    change flipMap s f ≫ e.hom = eRel s x' f e he ≫ repAct s G x' hX'
    exact hcompat.symm
  rw [h₁, h₂, op_comp, op_comp, G.map_comp, G.map_comp, types_comp_apply, types_comp_apply]
  congr 1
  change G.map fstS.op (G.map eS.op (univ s G x' hX')) = G.map flipS.op (G.map eS.op (univ s G x' hX'))
  rw [← types_comp_apply (G.map eS.op) (G.map fstS.op), ← G.map_comp, ← op_comp, h₃,
    ← types_comp_apply (G.map eS.op) (G.map flipS.op), ← G.map_comp, ← op_comp, h₄,
    op_comp, op_comp, G.map_comp, G.map_comp, types_comp_apply, types_comp_apply, map_repActOver_univ]

theorem existsUnique_univX (hG : Presieve.IsSheafFor G (Presieve.singleton (cover s f)))
    (hcompat : eRel s x' f e he ≫ repAct s G x' hX' = (canonical s f).act ≫ e.hom) :
    ∃! ζ : G.obj (op (Over.mk f)), G.map (cover s f).op ζ = univBC s G x' hX' f e he :=
  (Presieve.isSheafFor_singleton.1 hG) _ (fun p₁ p₂ hp => univBC_compatible s G x' hX' f e he hcompat p₁ p₂ hp)

noncomputable def coverT (T : Over S) : (Over.map s).obj ((Over.pullback s).obj T) ⟶ T :=
  Over.homMk (pullback.fst T.hom s) pullback.condition

theorem counit_app_eq_coverT (T : Over S) : (Over.mapPullbackAdj s).counit.app T = coverT s T := by
  ext
  simp [coverT]

theorem hom_mk_w {T : Over S} (m : T ⟶ Over.mk f) : m.left ≫ f = T.hom := Over.w m

noncomputable def bc {T : Over S} (m : T ⟶ Over.mk f) : (Over.pullback s).obj T ⟶ Over.mk x' :=
  (Over.pullback s).map m ≫ (eOver s x' f e he).hom

theorem bc_left {T : Over S} (m : T ⟶ Over.mk f) :
    (bc s x' f e he m).left = pullback.lift (pullback.fst T.hom s ≫ m.left) (pullback.snd T.hom s)
      (by rw [Category.assoc, hom_mk_w]; exact pullback.condition) ≫ e.hom := rfl

theorem coverT_comp {T : Over S} (m : T ⟶ Over.mk f) :
    coverT s T ≫ m = (Over.map s).map ((Over.pullback s).map m) ≫ cover s f := by
  ext
  change pullback.fst T.hom s ≫ m.left = pullback.lift (pullback.fst T.hom s ≫ m.left) (pullback.snd T.hom s) _ ≫
    pullback.fst f s
  exact (pullback.lift_fst _ _ _).symm

noncomputable abbrev leftHom {T : Over S} (n : (Over.pullback s).obj T ⟶ Over.mk x') : pullback T.hom s ⟶ X' := n.left

theorem leftHom_comp {T : Over S} (n : (Over.pullback s).obj T ⟶ Over.mk x') :
    leftHom s x' n ≫ x' = pullback.snd T.hom s := Over.w n

noncomputable def nRel {T : Over S} (n : (Over.pullback s).obj T ⟶ Over.mk x') :
    pullback (pullback.snd T.hom s ≫ s) s ⟶ pullback (x' ≫ s) s :=
  pullback.map _ _ _ _ (leftHom s x' n) (𝟙 _) (𝟙 _)
    (by rw [Category.comp_id, ← Category.assoc, leftHom_comp s x' n]) (by rw [Category.comp_id, Category.id_comp])

@[reassoc] theorem nRel_fst {T : Over S} (n : (Over.pullback s).obj T ⟶ Over.mk x') :
    nRel s x' n ≫ pullback.fst _ _ = pullback.fst _ _ ≫ leftHom s x' n := pullback.lift_fst _ _ _
@[reassoc] theorem nRel_snd {T : Over S} (n : (Over.pullback s).obj T ⟶ Over.mk x') :
    nRel s x' n ≫ pullback.snd _ _ = pullback.snd _ _ := (pullback.lift_snd _ _ _).trans (Category.comp_id _)

@[reassoc] theorem flipMap_comp_leftHom {T : Over S} (z : G.obj (op T)) :
    flipMap s T.hom ≫ leftHom s x' (hX'.homEquiv.symm (G.map (coverT s T).op z)) =
      nRel s x' (hX'.homEquiv.symm (G.map (coverT s T).op z)) ≫ repAct s G x' hX' := by
  set n := hX'.homEquiv.symm (G.map (coverT s T).op z) with hn
  have hz : hX'.homEquiv n = G.map (coverT s T).op z := Equiv.apply_symm_apply _ _
  let ET : Over S' := Over.mk (pullback.snd (pullback.snd T.hom s ≫ s) s)
  let flipO : ET ⟶ (Over.pullback s).obj T := Over.homMk (flipMap s T.hom) (flipMap_snd s T.hom)
  let nRelO : ET ⟶ Over.mk (pullback.snd (x' ≫ s) s) := Over.homMk (nRel s x' n) (nRel_snd s x' n)
  let fstTS : (Over.map s).obj ET ⟶ (Over.map s).obj ((Over.pullback s).obj T) :=
    Over.homMk (pullback.fst _ _) pullback.condition
  have h₁ : (Over.map s).map flipO ≫ coverT s T = fstTS ≫ coverT s T := by
    ext
    change flipMap s T.hom ≫ pullback.fst T.hom s = pullback.fst _ _ ≫ pullback.fst T.hom s
    exact flipMap_fst s T.hom
  have h₂ : (Over.map s).map nRelO ≫ fstOver s x' = fstTS ≫ (Over.map s).map n := by
    ext
    change nRel s x' n ≫ pullback.fst _ _ = pullback.fst _ _ ≫ leftHom s x' n
    exact nRel_fst s x' n
  have key : flipO ≫ n = nRelO ≫ repActOver s G x' hX' := by
    apply hX'.homEquiv.injective
    rw [hX'.homEquiv_comp, hX'.homEquiv_comp, hz, repActOver, Equiv.apply_symm_apply]
    change G.map ((Over.map s).map flipO).op (G.map (coverT s T).op z) =
      G.map ((Over.map s).map nRelO).op (G.map (fstOver s x').op (univ s G x' hX'))
    rw [← types_comp_apply (G.map _) (G.map ((Over.map s).map flipO).op), ← G.map_comp, ← op_comp, h₁,
      ← types_comp_apply (G.map _) (G.map ((Over.map s).map nRelO).op), ← G.map_comp, ← op_comp, h₂,
      op_comp, op_comp, G.map_comp, G.map_comp, types_comp_apply, types_comp_apply, ← homEquiv_eq_map_univ, hz]
  exact congrArg (fun k => k.left) key

theorem desc_condition
    (hcompat : eRel s x' f e he ≫ repAct s G x' hX' = (canonical s f).act ≫ e.hom)
    {T : Over S} (z : G.obj (op T)) {W : Scheme.{u}} (g₁ g₂ : W ⟶ pullback T.hom s)
    (hg : g₁ ≫ pullback.fst T.hom s = g₂ ≫ pullback.fst T.hom s) :
    g₁ ≫ (leftHom s x' (hX'.homEquiv.symm (G.map (coverT s T).op z)) ≫ e.inv ≫ pullback.fst f s) =
      g₂ ≫ (leftHom s x' (hX'.homEquiv.symm (G.map (coverT s T).op z)) ≫ e.inv ≫ pullback.fst f s) := by
  set n := hX'.homEquiv.symm (G.map (coverT s T).op z) with hn
  have w : g₁ ≫ pullback.snd T.hom s ≫ s = (g₂ ≫ pullback.snd T.hom s) ≫ s := by
    rw [Category.assoc, ← pullback.condition, reassoc_of% hg]
  let ρ : W ⟶ pullback (pullback.snd T.hom s ≫ s) s := pullback.lift g₁ (g₂ ≫ pullback.snd T.hom s) w
  have hρ₁ : g₁ = ρ ≫ pullback.fst _ _ := (pullback.lift_fst _ _ _).symm
  have hρ₂ : g₂ = ρ ≫ flipMap s T.hom := by
    apply pullback.hom_ext
    · rw [Category.assoc, flipMap_fst, pullback.lift_fst_assoc]
      exact hg.symm
    · rw [Category.assoc, flipMap_snd]
      exact (pullback.lift_snd _ _ _).symm
  have heinv : e.inv ≫ pullback.snd f s = x' := by rw [← he, Iso.inv_hom_id_assoc]

  let N : pullback T.hom s ⟶ pullback f s := leftHom s x' n ≫ e.inv
  have hN₂ : N ≫ pullback.snd f s = pullback.snd T.hom s := by
    change (leftHom s x' n ≫ e.inv) ≫ pullback.snd f s = _
    rw [Category.assoc, heinv, leftHom_comp]
  let NS : pullback (pullback.snd T.hom s ≫ s) s ⟶ pullback (pullback.snd f s ≫ s) s :=
    pullback.map _ _ _ _ N (𝟙 _) (𝟙 _) (by rw [Category.comp_id, reassoc_of% hN₂])
      (by rw [Category.comp_id, Category.id_comp])
  have hNS₁ : NS ≫ pullback.fst _ _ = pullback.fst _ _ ≫ N := pullback.lift_fst _ _ _
  have hNS₂ : NS ≫ pullback.snd _ _ = pullback.snd _ _ := (pullback.lift_snd _ _ _).trans (Category.comp_id _)
  have hN : NS ≫ eRel s x' f e he = nRel s x' n := by
    apply pullback.hom_ext
    · rw [Category.assoc, eRel_fst, nRel_fst, ← Category.assoc, hNS₁, Category.assoc]
      change pullback.fst _ _ ≫ (leftHom s x' n ≫ e.inv) ≫ e.hom = _
      rw [Category.assoc, Iso.inv_hom_id, Category.comp_id]
    · rw [Category.assoc, eRel_snd, nRel_snd, hNS₂]
  rw [hρ₁, hρ₂, Category.assoc, Category.assoc]
  congr 1

  rw [flipMap_comp_leftHom_assoc, ← hN, Category.assoc, reassoc_of% hcompat, Iso.hom_inv_id_assoc]
  change _ = NS ≫ flipMap s f ≫ pullback.fst f s
  rw [flipMap_fst, reassoc_of% hNS₁]
  simp only [N, Category.assoc]

section withζ

variable (ζ : G.obj (op (Over.mk f))) (hζ : G.map (cover s f).op ζ = univBC s G x' hX' f e he)

include hζ in

theorem map_coverT_map_eq_homEquiv_bc {T : Over S} (m : T ⟶ Over.mk f) :
    G.map (coverT s T).op (G.map m.op ζ) = hX'.homEquiv (bc s x' f e he m) := by
  have h1 := coverT_comp s f m
  calc G.map (coverT s T).op (G.map m.op ζ)
      = G.map ((Over.map s).map ((Over.pullback s).map m)).op (G.map (cover s f).op ζ) := by
        rw [← types_comp_apply (G.map m.op) (G.map _), ← G.map_comp, ← op_comp, h1, op_comp, G.map_comp,
          types_comp_apply]
    _ = G.map ((Over.map s).map ((Over.pullback s).map m)).op (univBC s G x' hX' f e he) :=
        congrArg (fun y => G.map ((Over.map s).map ((Over.pullback s).map m)).op y) hζ
    _ = hX'.homEquiv (bc s x' f e he m) := by
        rw [univBC, homEquiv_eq_map_univ, bc, Functor.map_comp, op_comp, G.map_comp, types_comp_apply]

include hζ in

theorem map_ζ_injective {T : Over S} [Epi (pullback.fst T.hom s)] {m₁ m₂ : T ⟶ Over.mk f}
    (h : G.map m₁.op ζ = G.map m₂.op ζ) : m₁ = m₂ := by
  have h' : bc s x' f e he m₁ = bc s x' f e he m₂ := by
    apply hX'.homEquiv.injective
    rw [← map_coverT_map_eq_homEquiv_bc s G x' hX' f e he ζ hζ, ← map_coverT_map_eq_homEquiv_bc s G x' hX' f e he ζ hζ,
      h]
  have h'' := congrArg (fun k => k.left) h'
  change (bc s x' f e he m₁).left = (bc s x' f e he m₂).left at h''
  rw [bc_left, bc_left] at h''
  have h₃ := (cancel_mono e.hom).1 h''
  have h₄ := congrArg (fun k => k ≫ pullback.fst f s) h₃
  have h₅ : pullback.fst T.hom s ≫ m₁.left = pullback.fst T.hom s ≫ m₂.left :=
    ((pullback.lift_fst _ _ _).symm.trans h₄).trans (pullback.lift_fst _ _ _)
  ext
  exact (cancel_epi (pullback.fst T.hom s)).1 h₅

include hζ in

theorem map_ζ_surjective (hcompat : eRel s x' f e he ≫ repAct s G x' hX' = (canonical s f).act ≫ e.hom)
    {T : Over S} (hsep : Presieve.IsSeparatedFor G (Presieve.singleton (coverT s T)))
    [EffectiveEpi (pullback.fst T.hom s)] (z : G.obj (op T)) :
    ∃ m : T ⟶ Over.mk f, G.map m.op ζ = z := by
  set n := hX'.homEquiv.symm (G.map (coverT s T).op z) with hn
  have hz : hX'.homEquiv n = G.map (coverT s T).op z := Equiv.apply_symm_apply _ _
  have heinv : e.inv ≫ pullback.snd f s = x' := by rw [← he, Iso.inv_hom_id_assoc]
  let N : pullback T.hom s ⟶ pullback f s := leftHom s x' n ≫ e.inv
  have hN₂ : N ≫ pullback.snd f s = pullback.snd T.hom s := by
    change (leftHom s x' n ≫ e.inv) ≫ pullback.snd f s = _
    rw [Category.assoc, heinv, leftHom_comp]
  let a : pullback T.hom s ⟶ X := leftHom s x' n ≫ e.inv ≫ pullback.fst f s
  let ml : T.left ⟶ X := EffectiveEpi.desc (pullback.fst T.hom s) a
    (fun g₁ g₂ hg => desc_condition s G x' hX' f e he hcompat z g₁ g₂ hg)
  have hml : pullback.fst T.hom s ≫ ml = a := EffectiveEpi.fac _ _ _
  have hmf : ml ≫ f = T.hom := by
    apply (cancel_epi (pullback.fst T.hom s)).1
    rw [← Category.assoc, hml]
    change ((leftHom s x' n ≫ e.inv) ≫ pullback.fst f s) ≫ f = _
    rw [Category.assoc, pullback.condition, ← Category.assoc, hN₂]
    exact pullback.condition.symm
  let m : T ⟶ Over.mk f := Over.homMk ml hmf
  have hlift : pullback.lift (pullback.fst T.hom s ≫ ml) (pullback.snd T.hom s)
      (by rw [Category.assoc, hmf]; exact pullback.condition) = N := by
    apply pullback.hom_ext
    · rw [pullback.lift_fst, hml]
      change (leftHom s x' n ≫ e.inv) ≫ pullback.fst f s = N ≫ pullback.fst f s
      rfl
    · rw [pullback.lift_snd, hN₂]
  have hbc : bc s x' f e he m = n := by
    ext
    change pullback.lift (pullback.fst T.hom s ≫ ml) (pullback.snd T.hom s) _ ≫ e.hom = leftHom s x' n
    rw [hlift]
    change (leftHom s x' n ≫ e.inv) ≫ e.hom = _
    rw [Category.assoc, Iso.inv_hom_id, Category.comp_id]
  refine ⟨m, ?_⟩
  apply Presieve.isSeparatedFor_singleton.1 hsep
  change G.map (coverT s T).op (G.map m.op ζ) = G.map (coverT s T).op z
  rw [map_coverT_map_eq_homEquiv_bc s G x' hX' f e he ζ hζ, hbc, hz]

end withζ

theorem representableBy_of_compatible
    (hG : ∀ T : Over S, Presieve.IsSheafFor G (Presieve.singleton (coverT s T)))
    [∀ T : Over S, EffectiveEpi (pullback.fst T.hom s)]
    (hcompat : eRel s x' f e he ≫ repAct s G x' hX' = (canonical s f).act ≫ e.hom) :
    Nonempty (G.RepresentableBy (Over.mk f)) := by
  obtain ⟨ζ, hζ, -⟩ := existsUnique_univX s G x' hX' f e he (hG (Over.mk f)) hcompat
  have hbij : ∀ T : Over S, Function.Bijective (fun m : T ⟶ Over.mk f => G.map m.op ζ) := fun T =>
    ⟨fun m₁ m₂ h => map_ζ_injective s G x' hX' f e he ζ hζ h,
      fun z => map_ζ_surjective s G x' hX' f e he ζ hζ hcompat (hG T).isSeparatedFor z⟩
  refine ⟨{ homEquiv := fun {T} => Equiv.ofBijective _ (hbij T), homEquiv_comp := ?_ }⟩
  intro T T' g m
  change G.map (g ≫ m).op ζ = G.map g.op (G.map m.op ζ)
  rw [op_comp, G.map_comp, types_comp_apply]

end rep

end DescentAction

end AlgebraicGeometry
