import Definitions.Def_FLTPrelim_Ramification
import Definitions.Def_EllipticCurve_FrobeniusTrace
import Mathlib.Algebra.MvPolynomial.CommRing
import Mathlib.Algebra.Module.Torsion.Basic
import Mathlib.LinearAlgebra.Determinant
import Mathlib.LinearAlgebra.Dimension.Finrank

set_option autoImplicit false

noncomputable section

namespace ModularCurve

abbrev HeckeAlg : Type := MvPolynomial Nat.Primes ℤ

def heckeGen (ℓ : Nat.Primes) : HeckeAlg := MvPolynomial.X ℓ

@[simp] lemma aeval_heckeGen {A : Type*} [CommSemiring A] [Algebra ℤ A]
    (a : Nat.Primes → A) (ℓ : Nat.Primes) :
    MvPolynomial.aeval a (heckeGen ℓ) = a ℓ :=
  MvPolynomial.aeval_X a ℓ

lemma adjoin_range_heckeGen : Algebra.adjoin ℤ (Set.range heckeGen) = ⊤ :=
  MvPolynomial.adjoin_range_X

section EigenIdeal

variable {k : Type*} [CommRing k]

def eigenIdeal (a : Nat.Primes → k) : Ideal HeckeAlg :=
  RingHom.ker (MvPolynomial.aeval (R := ℤ) a)

lemma mem_eigenIdeal_iff (a : Nat.Primes → k) (t : HeckeAlg) :
    t ∈ eigenIdeal a ↔ MvPolynomial.aeval a t = 0 :=
  RingHom.mem_ker

lemma heckeGen_sub_mem_eigenIdeal (a : Nat.Primes → k) (ℓ : Nat.Primes) (b : ℤ)
    (hb : algebraMap ℤ k b = a ℓ) :
    heckeGen ℓ - MvPolynomial.C b ∈ eigenIdeal a := by
  rw [mem_eigenIdeal_iff, map_sub, aeval_heckeGen, MvPolynomial.aeval_C, hb, sub_self]

end EigenIdeal

section Divisorial

variable {G : Type*} [Group G] (J : Type*) [AddCommGroup J] [Module HeckeAlg J]
  [DistribMulAction G J] [SMulCommClass G HeckeAlg J]

abbrev heckeTorsion (𝔪 : Ideal HeckeAlg) : Submodule HeckeAlg J :=
  Submodule.torsionBySet HeckeAlg J (𝔪 : Set HeckeAlg)

lemma mem_heckeTorsion_iff (𝔪 : Ideal HeckeAlg) (x : J) :
    x ∈ heckeTorsion J 𝔪 ↔ ∀ t ∈ 𝔪, t • x = 0 := by
  rw [Submodule.mem_torsionBySet_iff]
  exact ⟨fun h t ht => h ⟨t, ht⟩, fun h t => h t.1 t.2⟩

theorem smul_mem_heckeTorsion {𝔪 : Ideal HeckeAlg} (g : G) {x : J}
    (hx : x ∈ heckeTorsion J 𝔪) : g • x ∈ heckeTorsion J 𝔪 := by
  rw [Submodule.mem_torsionBySet_iff] at hx ⊢
  intro t
  rw [← smul_comm g (t : HeckeAlg) x, hx t, smul_zero]

def mTorsionGaloisRep (𝔪 : Ideal HeckeAlg) :
    G →* Module.End (HeckeAlg ⧸ 𝔪) (heckeTorsion J 𝔪) where
  toFun g :=
    { toFun := fun x => ⟨g • (x : J), smul_mem_heckeTorsion J g x.2⟩
      map_add' := fun x y => by
        ext
        simp [smul_add]
      map_smul' := fun r x => by
        obtain ⟨a, rfl⟩ := Ideal.Quotient.mk_surjective r
        ext
        simp only [Submodule.torsionBySet.mk_smul, RingHom.id_apply, Submodule.coe_smul]
        exact smul_comm g a (x : J) }
  map_one' := by
    ext x
    simp
  map_mul' g h := by
    ext x
    simp [mul_smul]

@[simp] theorem coe_mTorsionGaloisRep_apply (𝔪 : Ideal HeckeAlg) (g : G)
    (x : heckeTorsion J 𝔪) :
    ((mTorsionGaloisRep J 𝔪 g x : heckeTorsion J 𝔪) : J) = g • (x : J) := rfl

def eisensteinKernel (I : Ideal HeckeAlg) : Ideal HeckeAlg where
  carrier := {t | ∃ i ∈ I, ∀ x : J, ((1 + i) * t) • x = 0}
  zero_mem' := ⟨0, I.zero_mem, fun x => by simp⟩
  add_mem' := by
    rintro t t' ⟨i, hi, ht⟩ ⟨i', hi', ht'⟩
    refine ⟨i + i' + i * i', I.add_mem (I.add_mem hi hi') (I.mul_mem_left i hi'), fun x => ?_⟩
    have h1 : ((1 + i') * ((1 + i) * t)) • x = 0 := by rw [mul_smul, ht x, smul_zero]
    have h2 : ((1 + i) * ((1 + i') * t')) • x = 0 := by rw [mul_smul, ht' x, smul_zero]
    have : (1 + (i + i' + i * i')) * (t + t')
        = (1 + i') * ((1 + i) * t) + (1 + i) * ((1 + i') * t') := by
      ring
    rw [this, add_smul, h1, h2, add_zero]
  smul_mem' := by
    rintro c t ⟨i, hi, ht⟩
    refine ⟨i, hi, fun x => ?_⟩
    have : (1 + i) * (c • t) = c * ((1 + i) * t) := by rw [smul_eq_mul]; ring
    rw [this, mul_smul, ht x, smul_zero]

def EisensteinQuotientInvariantsFinite (I : Ideal HeckeAlg) : Prop :=
  Set.Finite ((eisensteinKernel J I • (⊤ : Submodule HeckeAlg J)).mkQ ''
    {x : J | ∀ σ : G, σ • x - x ∈ eisensteinKernel J I • (⊤ : Submodule HeckeAlg J)})

end Divisorial

section Forms

variable (𝕋 : Type*) [CommRing 𝕋] (J : Type*) [AddCommGroup J] [Module 𝕋 J] (𝔪 : Ideal 𝕋)

def MTorsionNeBot : Prop :=
  Submodule.torsionBySet 𝕋 J 𝔪 ≠ ⊥

end Forms

section LocalClauses

variable {K L : Type*} [Field K] [Field L] [Algebra K L]
variable (N p : ℕ) (J : Type*) [AddCommGroup J] [Module HeckeAlg J]
  [DistribMulAction (L ≃ₐ[K] L) J] [SMulCommClass (L ≃ₐ[K] L) HeckeAlg J]

def UnramifiedOutside : Prop :=
  ∀ ℓ : ℕ, ℓ.Prime → ¬ ℓ ∣ N * p →
    ∀ A : ValuationSubring L, A.LiesOverPrime ℓ →
      ∀ σ ∈ A.inertiaSubgroupIn K, ∀ x : J, (∃ n : ℕ, p ^ n • x = 0) → σ • x = x

def FrobeniusQuadratic : Prop :=
  ∀ ℓ : ℕ, (hℓ : ℓ.Prime) → ¬ ℓ ∣ N * p →
    ∀ A : ValuationSubring L, A.LiesOverPrime ℓ →
      ∀ σ : L ≃ₐ[K] L, A.IsFrobeniusAt σ ℓ →
        ∀ x : J, (∃ n : ℕ, p ^ n • x = 0) →
          σ • σ • x - heckeGen ⟨ℓ, hℓ⟩ • (σ • x) + ℓ • x = 0

def DetFrobeniusMod (𝔪 : Ideal HeckeAlg) : Prop :=
  ∀ ℓ : ℕ, ℓ.Prime → ¬ ℓ ∣ N * p →
    ∀ A : ValuationSubring L, A.LiesOverPrime ℓ →
      ∀ σ : L ≃ₐ[K] L, A.IsFrobeniusAt σ ℓ →
        LinearMap.det (mTorsionGaloisRep J 𝔪 σ) = ((ℓ : HeckeAlg ⧸ 𝔪))

end LocalClauses

section MultiplicityOne

variable (p : ℕ) (J : Type*) [AddCommGroup J] [Module HeckeAlg J]

structure MultiplicityOneData (𝔪 : Ideal HeckeAlg) : Prop where
  isMaximal : 𝔪.IsMaximal
  resChar : (p : HeckeAlg) ∈ 𝔪
  finrank_two : Module.finrank (HeckeAlg ⧸ 𝔪) (heckeTorsion J 𝔪) = 2

end MultiplicityOne

def eisensteinSystem (N : ℕ) : Nat.Primes → ℤ :=
  fun ℓ => if (ℓ : ℕ) ∣ N then 1 else 1 + (ℓ : ℕ)

@[simp] lemma eisensteinSystem_of_not_dvd {N : ℕ} {ℓ : Nat.Primes} (h : ¬ (ℓ : ℕ) ∣ N) :
    eisensteinSystem N ℓ = 1 + (ℓ : ℕ) := if_neg h

@[simp] lemma eisensteinSystem_of_dvd {N : ℕ} {ℓ : Nat.Primes} (h : (ℓ : ℕ) ∣ N) :
    eisensteinSystem N ℓ = 1 := if_pos h

def eisensteinIdeal (N : ℕ) : Ideal HeckeAlg :=
  eigenIdeal (eisensteinSystem N)

section Support

variable (p : ℕ) (J : Type*) [AddCommGroup J] [Module HeckeAlg J]

def EigenformSupport (IsEig : Ideal HeckeAlg → Prop) : Prop :=
  ∀ 𝔪 : Ideal HeckeAlg, IsEig 𝔪 → ((p : HeckeAlg)) ∈ 𝔪 → MTorsionNeBot HeckeAlg J 𝔪

end Support

section Bundle

variable {K L : Type*} [Field K] [Field L] [Algebra K L]
variable (N p : ℕ) (J : Type*) [AddCommGroup J] [Module HeckeAlg J]
  [DistribMulAction (L ≃ₐ[K] L) J] [SMulCommClass (L ≃ₐ[K] L) HeckeAlg J]

structure EichlerShimuraDataReduced (IsEig : Ideal HeckeAlg → Prop) : Prop where
  unramifiedOutside : UnramifiedOutside (K := K) (L := L) N p J
  frobeniusQuadratic : FrobeniusQuadratic (K := K) (L := L) N p J
  eigenformSupport : EigenformSupport p J IsEig

end Bundle

section Datum

variable {K L : Type*} [Field K] [Field L] [Algebra K L]
variable (p : ℕ) (J : Type*) [AddCommGroup J] [Module HeckeAlg J]
  [DistribMulAction (L ≃ₐ[K] L) J] [SMulCommClass (L ≃ₐ[K] L) HeckeAlg J]
variable (J' : Type*) [AddCommGroup J'] [Module HeckeAlg J']

structure IsSpecialization (A : ValuationSubring L) (ℓ : ℕ) (sp : J →+ J') (F : J' →+ J') :
    Prop where

  hecke_comm : ∀ (T : HeckeAlg) (x : J), sp (T • x) = T • sp x

  inertia_comm : ∀ σ ∈ A.inertiaSubgroupIn K, ∀ x : J, sp (σ • x) = sp x

  frobenius_comm : ∀ σ : L ≃ₐ[K] L, A.IsFrobeniusAt σ ℓ → ∀ x : J, sp (σ • x) = F (sp x)

  injective_torsion : ∀ x : J, (∃ n : ℕ, p ^ n • x = 0) → sp x = 0 → x = 0

def SpecialFibreRelation (ℓ : ℕ) (hℓ : ℓ.Prime) (F : J' →+ J') : Prop :=
  ∀ y : J', F (F y) - heckeGen ⟨ℓ, hℓ⟩ • F y + ℓ • y = 0

end Datum

section Citation

variable {K L : Type*} [Field K] [Field L] [Algebra K L]
variable (N p : ℕ) (J : Type*) [AddCommGroup J] [Module HeckeAlg J]
  [DistribMulAction (L ≃ₐ[K] L) J] [SMulCommClass (L ≃ₐ[K] L) HeckeAlg J]

def SpecializationExists : Prop :=
  ∀ ℓ : ℕ, (hℓ : ℓ.Prime) → ¬ ℓ ∣ N * p →
    ∀ A : ValuationSubring L, A.LiesOverPrime ℓ →
      ∃ (J' : Type) (_ : AddCommGroup J') (_ : Module HeckeAlg J') (sp : J →+ J')
        (F : J' →+ J'),
        IsSpecialization (K := K) p J J' A ℓ sp F ∧ SpecialFibreRelation J' ℓ hℓ F

end Citation

end ModularCurve
