import Mathlib.AlgebraicGeometry.Sites.ConstantSheaf
import Mathlib.AlgebraicGeometry.AffineSpace
import Mathlib.AlgebraicGeometry.Limits
import Mathlib.CategoryTheory.Sites.SubcanonicalOver
import Mathlib.CategoryTheory.Monoidal.Cartesian.CommGrp_
import Mathlib.Algebra.Category.Grp.Adjunctions
import Mathlib.Algebra.Category.Grp.EquivalenceGroupAddGroup

set_option autoImplicit false
set_option maxHeartbeats 800000
set_option synthInstance.maxHeartbeats 200000

noncomputable section

namespace FppfRepresentableGroupSchemeSheaf

open CategoryTheory AlgebraicGeometry Limits Opposite MonoidalCategory CartesianMonoidalCategory
open scoped CategoryTheory.MonObj

universe w v₁ u₁ u

section TypeValued

theorem isSheaf_fppf_of_isRepresentable (P : Scheme.{u}ᵒᵖ ⥤ Type u) [P.IsRepresentable] :
    Presheaf.IsSheaf Scheme.fppfTopology P := by
  rw [isSheaf_iff_isSheaf_of_type]
  exact GrothendieckTopology.Subcanonical.isSheaf_of_isRepresentable P

theorem isSheaf_fpqc_of_isRepresentable (P : Scheme.{u}ᵒᵖ ⥤ Type u) [P.IsRepresentable] :
    Presheaf.IsSheaf Scheme.fpqcTopology P := by
  rw [isSheaf_iff_isSheaf_of_type]
  exact GrothendieckTopology.Subcanonical.isSheaf_of_isRepresentable P

theorem isSheaf_fppf_yoneda (G : Scheme.{u}) :
    Presheaf.IsSheaf Scheme.fppfTopology (yoneda.obj G) :=
  isSheaf_fppf_of_isRepresentable _

theorem isSheaf_fppf_of_representableBy (P : Scheme.{u}ᵒᵖ ⥤ Type u) {G : Scheme.{u}}
    (h : P.RepresentableBy G) :
    Presheaf.IsSheaf Scheme.fppfTopology P :=
  have : P.IsRepresentable := h.isRepresentable
  isSheaf_fppf_of_isRepresentable P

theorem presieve_isSheaf_fppf_of_isRepresentable (P : Scheme.{u}ᵒᵖ ⥤ Type u)
    [P.IsRepresentable] : Presieve.IsSheaf Scheme.fppfTopology P :=
  GrothendieckTopology.Subcanonical.isSheaf_of_isRepresentable P

end TypeValued

section StructuredValues

theorem isSheaf_fppf_monCat_of_forget_isRepresentable (F : Scheme.{u}ᵒᵖ ⥤ MonCat.{u})
    [(F ⋙ forget MonCat).IsRepresentable] :
    Presheaf.IsSheaf Scheme.fppfTopology F :=
  Presheaf.isSheaf_of_isSheaf_comp _ _ (forget MonCat)
    (isSheaf_fppf_of_isRepresentable (F ⋙ forget MonCat))

theorem isSheaf_fppf_grpCat_of_forget_isRepresentable (F : Scheme.{u}ᵒᵖ ⥤ GrpCat.{u})
    [(F ⋙ forget GrpCat).IsRepresentable] :
    Presheaf.IsSheaf Scheme.fppfTopology F :=
  Presheaf.isSheaf_of_isSheaf_comp _ _ (forget GrpCat)
    (isSheaf_fppf_of_isRepresentable (F ⋙ forget GrpCat))

theorem isSheaf_fppf_commGrpCat_of_forget_isRepresentable (F : Scheme.{u}ᵒᵖ ⥤ CommGrpCat.{u})
    [(F ⋙ forget CommGrpCat).IsRepresentable] :
    Presheaf.IsSheaf Scheme.fppfTopology F :=
  Presheaf.isSheaf_of_isSheaf_comp _ _ (forget CommGrpCat)
    (isSheaf_fppf_of_isRepresentable (F ⋙ forget CommGrpCat))

theorem isSheaf_fppf_ab_of_forget_isRepresentable (F : Scheme.{u}ᵒᵖ ⥤ AddCommGrpCat.{u})
    [(F ⋙ forget AddCommGrpCat).IsRepresentable] :
    Presheaf.IsSheaf Scheme.fppfTopology F :=
  Presheaf.isSheaf_of_isSheaf_comp _ _ (forget AddCommGrpCat)
    (isSheaf_fppf_of_isRepresentable (F ⋙ forget AddCommGrpCat))

end StructuredValues

section GroupSchemeOverZ

variable (G : Scheme.{u}) [GrpObj G]

theorem isSheaf_fppf_yonedaGrpObj :
    Presheaf.IsSheaf Scheme.fppfTopology (yonedaGrpObj G) :=
  have : (yonedaGrpObj G ⋙ forget GrpCat).IsRepresentable :=
    (yonedaGrpObjRepresentableBy G).isRepresentable
  isSheaf_fppf_grpCat_of_forget_isRepresentable _

def fppfGrpSheaf : Sheaf Scheme.fppfTopology GrpCat.{u} :=
  ⟨yonedaGrpObj G, isSheaf_fppf_yonedaGrpObj G⟩

variable [IsCommMonObj G]

def yonedaCommGrpObj : Scheme.{u}ᵒᵖ ⥤ CommGrpCat.{u} where
  obj T := CommGrpCat.of (unop T ⟶ G)
  map φ := CommGrpCat.ofHom ((yonedaGrpObj G).map φ).hom
  map_id _ := CommGrpCat.hom_ext (MonoidHom.ext fun g => Category.id_comp g)
  map_comp φ ψ := CommGrpCat.hom_ext (MonoidHom.ext fun g => Category.assoc ψ.unop φ.unop g)

def yonedaCommGrpObjRepresentableBy :
    (yonedaCommGrpObj G ⋙ forget CommGrpCat).RepresentableBy G where
  homEquiv := Equiv.refl _
  homEquiv_comp _ _ := rfl

theorem isSheaf_fppf_yonedaCommGrpObj :
    Presheaf.IsSheaf Scheme.fppfTopology (yonedaCommGrpObj G) :=
  have : (yonedaCommGrpObj G ⋙ forget CommGrpCat).IsRepresentable :=
    (yonedaCommGrpObjRepresentableBy G).isRepresentable
  isSheaf_fppf_commGrpCat_of_forget_isRepresentable _

def fppfCommGrpSheaf : Sheaf Scheme.fppfTopology CommGrpCat.{u} :=
  ⟨yonedaCommGrpObj G, isSheaf_fppf_yonedaCommGrpObj G⟩

def yonedaAddCommGrpObj : Scheme.{u}ᵒᵖ ⥤ AddCommGrpCat.{u} :=
  yonedaCommGrpObj G ⋙ commGroupAddCommGroupEquivalence.functor

theorem isSheaf_fppf_yonedaAddCommGrpObj :
    Presheaf.IsSheaf Scheme.fppfTopology (yonedaAddCommGrpObj G) :=
  Presheaf.isSheaf_comp_of_isSheaf _ _ commGroupAddCommGroupEquivalence.functor
    (isSheaf_fppf_yonedaCommGrpObj G)

def fppfAbelianSheaf : Sheaf Scheme.fppfTopology AddCommGrpCat.{u} :=
  ⟨yonedaAddCommGrpObj G, isSheaf_fppf_yonedaAddCommGrpObj G⟩

end GroupSchemeOverZ

section SectionsIdentification

variable (G : Scheme.{u}) [GrpObj G]

def fppfGrpSheafSectionsMulEquiv (T : Scheme.{u}) :
    ((fppfGrpSheaf G).obj.obj (op T)) ≃* (T ⟶ G) :=
  MulEquiv.refl _

variable [IsCommMonObj G]

def fppfCommGrpSheafSectionsMulEquiv (T : Scheme.{u}) :
    ((fppfCommGrpSheaf G).obj.obj (op T)) ≃* (T ⟶ G) :=
  MulEquiv.refl _

def fppfAbelianSheafSectionsAddEquiv (T : Scheme.{u}) :
    ((fppfAbelianSheaf G).obj.obj (op T)) ≃+ Additive (T ⟶ G) :=
  AddEquiv.refl _

def fppfAbelianSheafGlobalSectionsAddEquiv :
    ((fppfAbelianSheaf G).obj.obj (op (𝟙_ Scheme.{u}))) ≃+ Additive (𝟙_ Scheme.{u} ⟶ G) :=
  fppfAbelianSheafSectionsAddEquiv G (𝟙_ Scheme.{u})

end SectionsIdentification

section GroupSchemeOverBase

variable (S : Scheme.{u})

abbrev fppfTopologyOver : GrothendieckTopology (Over S) :=
  Scheme.fppfTopology.over S

example : (fppfTopologyOver S).Subcanonical := inferInstance

variable (G : Over S) [GrpObj G]

theorem isSheaf_fppfOver_yonedaGrpObj :
    Presheaf.IsSheaf (fppfTopologyOver S) (yonedaGrpObj G) :=
  have : (yonedaGrpObj G ⋙ forget GrpCat).IsRepresentable :=
    (yonedaGrpObjRepresentableBy G).isRepresentable
  Presheaf.isSheaf_of_isSheaf_comp _ _ (forget GrpCat) (by
    rw [isSheaf_iff_isSheaf_of_type]
    exact GrothendieckTopology.Subcanonical.isSheaf_of_isRepresentable _)

def fppfOverGrpSheaf : Sheaf (fppfTopologyOver S) GrpCat.{u} :=
  ⟨yonedaGrpObj G, isSheaf_fppfOver_yonedaGrpObj S G⟩

variable [IsCommMonObj G]

def yonedaCommGrpObjOver : (Over S)ᵒᵖ ⥤ CommGrpCat.{u} where
  obj T := CommGrpCat.of (unop T ⟶ G)
  map φ := CommGrpCat.ofHom ((yonedaGrpObj G).map φ).hom
  map_id _ := CommGrpCat.hom_ext (MonoidHom.ext fun g => Category.id_comp g)
  map_comp φ ψ := CommGrpCat.hom_ext (MonoidHom.ext fun g => Category.assoc ψ.unop φ.unop g)

def yonedaCommGrpObjOverRepresentableBy :
    (yonedaCommGrpObjOver S G ⋙ forget CommGrpCat).RepresentableBy G where
  homEquiv := Equiv.refl _
  homEquiv_comp _ _ := rfl

theorem isSheaf_fppfOver_yonedaCommGrpObjOver :
    Presheaf.IsSheaf (fppfTopologyOver S) (yonedaCommGrpObjOver S G) :=
  have : (yonedaCommGrpObjOver S G ⋙ forget CommGrpCat).IsRepresentable :=
    (yonedaCommGrpObjOverRepresentableBy S G).isRepresentable
  Presheaf.isSheaf_of_isSheaf_comp _ _ (forget CommGrpCat) (by
    rw [isSheaf_iff_isSheaf_of_type]
    exact GrothendieckTopology.Subcanonical.isSheaf_of_isRepresentable _)

def yonedaAddCommGrpObjOver : (Over S)ᵒᵖ ⥤ AddCommGrpCat.{u} :=
  yonedaCommGrpObjOver S G ⋙ commGroupAddCommGroupEquivalence.functor

theorem isSheaf_fppfOver_yonedaAddCommGrpObjOver :
    Presheaf.IsSheaf (fppfTopologyOver S) (yonedaAddCommGrpObjOver S G) :=
  Presheaf.isSheaf_comp_of_isSheaf _ _ commGroupAddCommGroupEquivalence.functor
    (isSheaf_fppfOver_yonedaCommGrpObjOver S G)

def fppfOverAbelianSheaf : Sheaf (fppfTopologyOver S) AddCommGrpCat.{u} :=
  ⟨yonedaAddCommGrpObjOver S G, isSheaf_fppfOver_yonedaAddCommGrpObjOver S G⟩

def fppfOverAbelianSheafSectionsAddEquiv (T : Over S) :
    ((fppfOverAbelianSheaf S G).obj.obj (op T)) ≃+ Additive (T ⟶ G) :=
  AddEquiv.refl _

end GroupSchemeOverBase

section UnitsDescent

variable {C : Type u₁} [Category.{v₁} C]

def unitsPresheaf (O : Cᵒᵖ ⥤ CommRingCat.{w}) : Cᵒᵖ ⥤ CommGrpCat.{w} :=
  O ⋙ forget₂ CommRingCat CommMonCat ⋙ CommMonCat.units

theorem presieve_isSheaf_units {J : GrothendieckTopology C} {O : Cᵒᵖ ⥤ CommRingCat.{w}}
    (hO : Presieve.IsSheaf J (O ⋙ forget CommRingCat)) :
    Presieve.IsSheaf J (unitsPresheaf O ⋙ forget CommGrpCat) := by
  intro X S hS x hx

  let xu : ∀ ⦃Y : C⦄ (f : Y ⟶ X), S.arrows f → ((O.obj (op Y)) : Type w)ˣ := fun _ f hf => x f hf

  let v : Presieve.FamilyOfElements (O ⋙ forget CommRingCat) S.arrows :=
    fun Y f hf => ((xu f hf : ((O.obj (op Y)) : Type w)ˣ) : (O.obj (op Y)))
  let w' : Presieve.FamilyOfElements (O ⋙ forget CommRingCat) S.arrows :=
    fun Y f hf => (((xu f hf)⁻¹ : ((O.obj (op Y)) : Type w)ˣ) : (O.obj (op Y)))

  have hv : v.Compatible := by
    intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ h₁ h₂ hcomm
    exact congrArg Units.val (hx g₁ g₂ h₁ h₂ hcomm)
  have hw : w'.Compatible := by
    intro Y₁ Y₂ Z g₁ g₂ f₁ f₂ h₁ h₂ hcomm
    exact congrArg (fun u : ((O.obj (op Z)) : Type w)ˣ => ((u⁻¹ : _ˣ) : (O.obj (op Z))))
      (hx g₁ g₂ h₁ h₂ hcomm)

  obtain ⟨a, ha, ha_uniq⟩ := hO S hS v hv
  obtain ⟨b, hb, -⟩ := hO S hS w' hw

  have hab : a * b = 1 := by
    apply (hO S hS).isSeparatedFor.ext
    intro Y f hf
    have h1 : (O.map f.op).hom (a * b) =
        (O.map f.op).hom a * (O.map f.op).hom b := map_mul _ a b
    have h2 : (O.map f.op).hom (1 : (O.obj (op X))) = 1 := map_one _
    have ha' : (O.map f.op).hom a = v f hf := ha f hf
    have hb' : (O.map f.op).hom b = w' f hf := hb f hf
    show (O.map f.op).hom (a * b) = (O.map f.op).hom (1 : (O.obj (op X)))
    rw [h1, h2, ha', hb']
    exact Units.mul_inv _
  have hba : b * a = 1 := by rw [mul_comm]; exact hab

  refine ⟨(⟨a, b, hab, hba⟩ : ((O.obj (op X)) : Type w)ˣ), ?_, ?_⟩
  ·
    intro Y f hf
    show Units.map (O.map f.op).hom.toMonoidHom (⟨a, b, hab, hba⟩ : ((O.obj (op X)) : Type w)ˣ)
      = xu f hf
    apply Units.ext
    show (O.map f.op).hom a = ((xu f hf : ((O.obj (op Y)) : Type w)ˣ) : (O.obj (op Y)))
    exact ha f hf
  ·
    intro u hu

    let u' : ((O.obj (op X)) : Type w)ˣ := u
    have hval : (u' : (O.obj (op X))) = a := by
      apply ha_uniq
      intro Y f hf
      exact congrArg Units.val (hu f hf)
    show u' = (⟨a, b, hab, hba⟩ : ((O.obj (op X)) : Type w)ˣ)
    exact Units.ext hval

end UnitsDescent

section Instances

abbrev affineLineOverZ : Scheme.{u} :=
  Spec (CommRingCat.of (MvPolynomial PUnit.{u + 1} (ULift.{u} ℤ)))

def gammaRepresentableBy :
    (Scheme.Γ.{u} ⋙ forget CommRingCat).RepresentableBy affineLineOverZ.{u} where
  homEquiv {X} := (AffineSpace.toSpecMvPolyIntEquiv PUnit.{u + 1} (X := X)).trans
    (Equiv.funUnique PUnit.{u + 1} _)
  homEquiv_comp f g := by
    show AffineSpace.toSpecMvPolyIntEquiv _ (f ≫ g) PUnit.unit = _
    rw [AffineSpace.toSpecMvPolyIntEquiv_comp]
    rfl

theorem presieve_isSheaf_fppf_gammaForget :
    Presieve.IsSheaf Scheme.fppfTopology.{u} (Scheme.Γ ⋙ forget CommRingCat) :=
  have : (Scheme.Γ.{u} ⋙ forget CommRingCat).IsRepresentable :=
    gammaRepresentableBy.{u}.isRepresentable
  GrothendieckTopology.Subcanonical.isSheaf_of_isRepresentable _

def Gm : Scheme.{u}ᵒᵖ ⥤ CommGrpCat.{u} :=
  unitsPresheaf Scheme.Γ

theorem isSheaf_fppf_Gm : Presheaf.IsSheaf Scheme.fppfTopology.{u} Gm.{u} := by
  apply Presheaf.isSheaf_of_isSheaf_comp _ _ (forget CommGrpCat)
  rw [isSheaf_iff_isSheaf_of_type]
  exact presieve_isSheaf_units presieve_isSheaf_fppf_gammaForget

def GmSheaf : Sheaf Scheme.fppfTopology.{u} CommGrpCat.{u} :=
  ⟨Gm, isSheaf_fppf_Gm⟩

def GmSectionsMulEquiv (X : Scheme.{u}) :
    ((Gm.{u}).obj (op X) : Type u) ≃* ((Γ(X, ⊤) : Type u))ˣ :=
  MulEquiv.refl _

def GmAbelianSheaf : Sheaf Scheme.fppfTopology.{u} AddCommGrpCat.{u} :=
  ⟨Gm ⋙ commGroupAddCommGroupEquivalence.functor,
    Presheaf.isSheaf_comp_of_isSheaf _ _ commGroupAddCommGroupEquivalence.functor
      isSheaf_fppf_Gm⟩

def mu (n : ℕ) : Scheme.{u}ᵒᵖ ⥤ Type u :=
  Gm ⋙ coyoneda.obj (op (CommGrpCat.of (ULift.{u} (Multiplicative (ZMod n)))))

theorem presieve_isSheaf_fppf_mu (n : ℕ) :
    Presieve.IsSheaf Scheme.fppfTopology.{u} (mu.{u} n) :=
  isSheaf_fppf_Gm (CommGrpCat.of (ULift.{u} (Multiplicative (ZMod n))))

theorem mu_obj (n : ℕ) (X : Scheme.{u}) :
    (mu.{u} n).obj (op X) =
      (CommGrpCat.of (ULift.{u} (Multiplicative (ZMod n))) ⟶ Gm.{u}.obj (op X)) :=
  rfl

section ConstantZMod

variable (n : ℕ)

local instance : TopologicalSpace (ZMod n) := ⊥

local instance : DiscreteTopology (ZMod n) := ⟨rfl⟩

local instance : IsTopologicalAddGroup (ZMod n) where
  continuous_add := continuous_of_discreteTopology
  continuous_neg := continuous_of_discreteTopology

def constantZMod : Scheme.{u}ᵒᵖ ⥤ AddCommGrpCat.{u} :=
  continuousMapPresheafAb (ZMod n)

theorem isSheaf_fppf_constantZMod :
    Presheaf.IsSheaf Scheme.fppfTopology.{u} (constantZMod.{u} n) :=
  Presheaf.IsSheaf.of_le Scheme.fppfTopology_le_fpqcTopology
    (isSheaf_fpqcTopology_continuousMapPresheafAb (ZMod n))

def constantZModSheaf : Sheaf Scheme.fppfTopology.{u} AddCommGrpCat.{u} :=
  ⟨constantZMod n, isSheaf_fppf_constantZMod n⟩

end ConstantZMod

end Instances

section Gates

instance : IsCommMonObj (𝟙_ Scheme.{u}) where
  mul_comm := isTerminalTensorUnit.hom_ext _ _

def gateTrivialGroupSchemeAbelianSheaf : Sheaf Scheme.fppfTopology AddCommGrpCat.{u} :=
  fppfAbelianSheaf (𝟙_ Scheme.{u})

instance gateTrivialSheafSectionsSubsingleton (T : Scheme.{u}) :
    Subsingleton ((gateTrivialGroupSchemeAbelianSheaf.{u}).obj.obj (op T)) :=
  ⟨fun a b => by
    have h : ∀ x y : Additive (T ⟶ 𝟙_ Scheme.{u}), x = y := fun x y =>
      congrArg Additive.ofMul (isTerminalTensorUnit.hom_ext x.toMul y.toMul)
    exact (fppfAbelianSheafSectionsAddEquiv (𝟙_ Scheme.{u}) T).injective (h _ _)⟩

theorem gate_singleton_generates_fppf_cover {X Y : Scheme.{u}} (f : X ⟶ Y) [Flat f]
    [Surjective f] [LocallyOfFinitePresentation f] :
    Sieve.generate (Presieve.singleton f) ∈ Scheme.fppfTopology Y :=
  Precoverage.generate_mem_toGrothendieck (Scheme.Hom.singleton_mem_fppfPrecoverage f)

def gateGmSectionsSpecZ :
    ((Gm.{0}).obj (op (Spec (CommRingCat.of ℤ))) : Type 0) ≃* ℤˣ :=
  (GmSectionsMulEquiv (Spec (CommRingCat.of ℤ))).trans
    (Units.mapEquiv (Scheme.ΓSpecIso (CommRingCat.of ℤ)).commRingCatIsoToRingEquiv.toMulEquiv)

theorem gate_Gm_sections_nontrivial :
    Nontrivial ((Gm.{0}).obj (op (Spec (CommRingCat.of ℤ))) : Type 0) :=
  haveI : Nontrivial ℤˣ := ⟨1, -1, by decide⟩
  gateGmSectionsSpecZ.toEquiv.nontrivial

end Gates

end FppfRepresentableGroupSchemeSheaf
