import Mathlib
import Definitions.Def_HopfAlgebra_CartierDual
import Definitions.Def_FiniteFlat_SchematicClosure

set_option autoImplicit false

open scoped TensorProduct

namespace HopfAlgebra

section FieldLevel

variable {F : Type*} [CommRing F] {A : Type*} [CommRing A] [Algebra F A]
variable {L : Type*} [CommRing L] [Algebra F L]

def vanishingIdealOfPoints (S : Set (A →ₐ[F] L)) : Ideal A where
  carrier := {a | ∀ ν ∈ S, ν a = 0}
  add_mem' {a b} ha hb := fun ν hν => by rw [map_add, ha ν hν, hb ν hν, add_zero]
  zero_mem' := fun ν _ => map_zero ν
  smul_mem' c {a} ha := fun ν hν => by rw [smul_eq_mul, map_mul, ha ν hν, mul_zero]

@[simp] theorem mem_vanishingIdealOfPoints_iff (S : Set (A →ₐ[F] L)) (a : A) :
    a ∈ vanishingIdealOfPoints S ↔ ∀ ν ∈ S, ν a = 0 := Iff.rfl

theorem vanishingIdealOfPoints_antitone {S T : Set (A →ₐ[F] L)} (h : S ⊆ T) :
    vanishingIdealOfPoints T ≤ vanishingIdealOfPoints S := fun _ ha ν hν => ha ν (h hν)

def liftPoint (S : Set (A →ₐ[F] L)) (ν : A →ₐ[F] L) (hν : ν ∈ S) :
    (A ⧸ vanishingIdealOfPoints S) →ₐ[F] L :=
  Ideal.Quotient.liftₐ (vanishingIdealOfPoints S) ν (fun a ha => ha ν hν)

@[simp] theorem liftPoint_mk (S : Set (A →ₐ[F] L)) (ν : A →ₐ[F] L) (hν : ν ∈ S) (a : A) :
    liftPoint S ν hν (Ideal.Quotient.mk (vanishingIdealOfPoints S) a) = ν a := rfl

noncomputable def evalPair (S : Set (A →ₐ[F] L)) (ν ν' : A →ₐ[F] L) (hν : ν ∈ S) (hν' : ν' ∈ S) :
    (A ⧸ vanishingIdealOfPoints S) ⊗[F] (A ⧸ vanishingIdealOfPoints S) →ₐ[F] L :=
  (Algebra.TensorProduct.lmul' F (S := L)).comp
    (Algebra.TensorProduct.map (liftPoint S ν hν) (liftPoint S ν' hν'))

theorem evalPair_tmul (S : Set (A →ₐ[F] L)) (ν ν' : A →ₐ[F] L) (hν : ν ∈ S) (hν' : ν' ∈ S) (a b : A) :
    evalPair S ν ν' hν hν' (Ideal.Quotient.mk _ a ⊗ₜ[F] Ideal.Quotient.mk _ b) = ν a * ν' b := by
  simp only [evalPair, AlgHom.coe_comp, Function.comp_apply, Algebra.TensorProduct.map_tmul, liftPoint_mk,
    Algebra.TensorProduct.lmul'_apply_tmul]

end FieldLevel

section PointMonoid

variable {F : Type*} [CommRing F] {A : Type*} [CommRing A] [Bialgebra F A]
variable {L : Type*} [CommRing L] [Algebra F L]

def ptSet (S : Submonoid (WithConv (A →ₐ[F] L))) : Set (A →ₐ[F] L) := {ν | WithConv.toConv ν ∈ S}

@[simp] theorem mem_ptSet_iff (S : Submonoid (WithConv (A →ₐ[F] L))) (ν : A →ₐ[F] L) :
    ν ∈ ptSet S ↔ WithConv.toConv ν ∈ S := Iff.rfl

theorem ofConv_mem_ptSet {S : Submonoid (WithConv (A →ₐ[F] L))} (ν : ↥S) : WithConv.ofConv ν.1 ∈ ptSet S := by
  show WithConv.toConv (WithConv.ofConv ν.1) ∈ S
  exact ν.2

theorem ptSet_mono {S T : Submonoid (WithConv (A →ₐ[F] L))} (h : S ≤ T) : ptSet S ⊆ ptSet T :=
  fun _ hν => h hν

abbrev pointQuot (S : Submonoid (WithConv (A →ₐ[F] L))) : Type _ := A ⧸ vanishingIdealOfPoints (ptSet S)

noncomputable def evalQuot (S : Submonoid (WithConv (A →ₐ[F] L))) : L ⊗[F] pointQuot S →ₐ[L] (↥S → L) :=
  Algebra.TensorProduct.lift (Algebra.ofId L _)
    (Pi.algHom F _ fun ν => liftPoint (ptSet S) (WithConv.ofConv ν.1) (ofConv_mem_ptSet ν))
    (fun _ _ => Commute.all _ _)

theorem evalQuot_tmul (S : Submonoid (WithConv (A →ₐ[F] L))) (c : L) (a : A) (ν : ↥S) :
    evalQuot S (c ⊗ₜ[F] Ideal.Quotient.mk _ a) ν = c * (WithConv.ofConv ν.1) a := by
  simp only [evalQuot, Algebra.TensorProduct.lift_tmul, Pi.mul_apply, Pi.algHom_apply]
  rw [Algebra.ofId_apply, Pi.algebraMap_apply, Algebra.algebraMap_self, RingHom.id_apply]
  rfl

end PointMonoid

end HopfAlgebra

namespace CartierDual

section BaseChange

universe u v w

variable (O : Type u) [CommRing O] (F : Type v) [CommRing F] [Algebra O F]
variable (A : Type w) [CommRing A] [Bialgebra O A]

theorem add_apply_pt {R : Type*} [CommRing R] {X : Type*} [CommRing X] [Bialgebra R X]
    (φ ψ : CartierDual R X) (x : X) : (φ + ψ) x = φ x + ψ x := by
  rw [← CartierDual.toDual_apply (φ + ψ), map_add, LinearMap.add_apply, CartierDual.toDual_apply,
    CartierDual.toDual_apply]

theorem zero_apply_pt {R : Type*} [CommRing R] {X : Type*} [CommRing X] [Bialgebra R X]
    (x : X) : (0 : CartierDual R X) x = 0 := by
  rw [← CartierDual.toDual_apply (0 : CartierDual R X), map_zero, LinearMap.zero_apply]

theorem smul_apply_pt {R : Type*} [CommRing R] {X : Type*} [CommRing X] [Bialgebra R X]
    (c : R) (φ : CartierDual R X) (x : X) : (c • φ) x = c * φ x := by
  rw [← CartierDual.toDual_apply (c • φ), LinearEquiv.map_smul, LinearMap.smul_apply, smul_eq_mul,
    CartierDual.toDual_apply]

noncomputable instance instModuleRestrictBaseChange : Module O (CartierDual F (F ⊗[O] A)) :=
  Module.compHom (CartierDual F (F ⊗[O] A)) (algebraMap O F)

instance instIsScalarTowerRestrictBaseChange : IsScalarTower O F (CartierDual F (F ⊗[O] A)) :=
  IsScalarTower.of_algebraMap_smul fun _ _ => rfl

noncomputable instance instRingBaseChangeDual : Ring (F ⊗[O] CartierDual O A) := Algebra.TensorProduct.instRing

noncomputable instance instAlgebraBaseChangeDual : Algebra F (F ⊗[O] CartierDual O A) :=
  Algebra.TensorProduct.leftAlgebra

variable {O F A}

noncomputable def dualBaseChange (φ : CartierDual O A) : CartierDual F (F ⊗[O] A) :=
  CartierDual.ofDual F (F ⊗[O] A)
    ((TensorProduct.AlgebraTensorModule.rid O F F).toLinearMap ∘ₗ LinearMap.baseChange F (CartierDual.toDual O A φ))

@[simp] theorem dualBaseChange_tmul (φ : CartierDual O A) (c : F) (a : A) :
    dualBaseChange (F := F) φ (c ⊗ₜ[O] a) = c * algebraMap O F (φ a) := by
  show (TensorProduct.AlgebraTensorModule.rid O F F) ((LinearMap.baseChange F (CartierDual.toDual O A φ)) (c ⊗ₜ[O] a)) = _
  rw [LinearMap.baseChange_tmul, TensorProduct.AlgebraTensorModule.rid_tmul, CartierDual.toDual_apply, Algebra.smul_def,
    mul_comm]

variable (O F A) in

noncomputable def dualBaseChangeHom : CartierDual O A →ₗ[O] CartierDual F (F ⊗[O] A) where
  toFun := dualBaseChange
  map_add' φ ψ := by
    refine CartierDual.ext fun x => ?_
    rw [add_apply_pt]
    induction x using TensorProduct.induction_on with
    | zero => rw [map_zero, map_zero, map_zero, add_zero]
    | tmul c a =>
      rw [dualBaseChange_tmul, dualBaseChange_tmul, dualBaseChange_tmul, add_apply_pt, map_add, mul_add]
    | add x y hx hy => rw [map_add, map_add, map_add, hx, hy]; abel
  map_smul' r φ := by
    refine CartierDual.ext fun x => ?_
    rw [RingHom.id_apply]
    show dualBaseChange (r • φ) x = ((algebraMap O F r) • dualBaseChange φ) x
    rw [smul_apply_pt]
    induction x using TensorProduct.induction_on with
    | zero => rw [map_zero, map_zero, mul_zero]
    | tmul c a => rw [dualBaseChange_tmul, dualBaseChange_tmul, smul_apply_pt, map_mul]; ring
    | add x y hx hy => rw [map_add, map_add, hx, hy, mul_add]

variable (O F A) in

noncomputable def dualBaseChangeLin : F ⊗[O] CartierDual O A →ₗ[F] CartierDual F (F ⊗[O] A) :=
  LinearMap.liftBaseChange F (dualBaseChangeHom O F A)

@[simp] theorem dualBaseChangeLin_tmul (c : F) (φ : CartierDual O A) :
    dualBaseChangeLin O F A (c ⊗ₜ[O] φ) = c • dualBaseChange φ :=
  LinearMap.liftBaseChange_tmul F _ c φ

theorem dualBaseChangeLin_tmul_tmul (c : F) (φ : CartierDual O A) (c' : F) (a : A) :
    dualBaseChangeLin O F A (c ⊗ₜ[O] φ) (c' ⊗ₜ[O] a) = c * c' * algebraMap O F (φ a) := by
  rw [dualBaseChangeLin_tmul, smul_apply_pt, dualBaseChange_tmul, mul_assoc]

end BaseChange

end CartierDual

namespace HopfAlgebra

section Character

universe u v w

variable (O : Type u) [CommRing O] (F : Type v) [CommRing F] [Algebra O F]
variable (A : Type w) [CommRing A] [Bialgebra O A]
variable (L : Type*) [CommRing L] [Algebra F L]

noncomputable def characterGenericFibre (S : Set (F ⊗[O] A →ₐ[F] L)) : Subalgebra F (F ⊗[O] CartierDual O A) :=
  Algebra.adjoin F {w | ∀ x ∈ vanishingIdealOfPoints S, CartierDual.dualBaseChangeLin O F A w x = 0}

theorem characterGenericFibre_mono {S T : Set (F ⊗[O] A →ₐ[F] L)} (h : S ⊆ T) :
    characterGenericFibre O F A L S ≤ characterGenericFibre O F A L T := by
  apply Algebra.adjoin_mono
  intro w hw x hx
  exact hw x (vanishingIdealOfPoints_antitone h hx)

theorem subset_characterGenericFibre (S : Set (F ⊗[O] A →ₐ[F] L)) :
    {w | ∀ x ∈ vanishingIdealOfPoints S, CartierDual.dualBaseChangeLin O F A w x = 0}
      ⊆ (characterGenericFibre O F A L S : Set (F ⊗[O] CartierDual O A)) :=
  Algebra.subset_adjoin

end Character

section Closure

universe u v w

variable (O : Type u) [CommRing O] (F : Type v) [Field F] [Algebra O F]
variable (A : Type w) [CommRing A] [Bialgebra O A] [Coalgebra.IsCocomm O A]
variable (L : Type*) [CommRing L] [Algebra F L]

noncomputable abbrev characterClosure (S : Set (F ⊗[O] A →ₐ[F] L)) : Subalgebra O (CartierDual O A) :=
  flatClosure (characterGenericFibre O F A L S)

theorem mem_characterClosure_iff (S : Set (F ⊗[O] A →ₐ[F] L)) (g : CartierDual O A) :
    g ∈ characterClosure O F A L S ↔ (1 : F) ⊗ₜ[O] g ∈ characterGenericFibre O F A L S := Iff.rfl

theorem characterClosure_mono {S T : Set (F ⊗[O] A →ₐ[F] L)} (h : S ⊆ T) :
    characterClosure O F A L S ≤ characterClosure O F A L T :=
  flatClosure_mono (characterGenericFibre_mono O F A L h)

end Closure

end HopfAlgebra
