import Mathlib
import Definitions.Def_ModularCurve_JqCoeff
import Definitions.Def_AlgebraicCurve_RatFuncPlaces
import Definitions.Def_AlgebraicCurve_DivisorPushPull
import Theorems.Thm_ModularCurve_qExpand_jqModC_eq_pow_unconditional
import Definitions.Def_ModularCurve_CharLFrobeniusGeomLevel

set_option autoImplicit false
set_option synthInstance.maxHeartbeats 400000
set_option maxHeartbeats 800000

noncomputable section

open AlgebraicCurve

namespace ModularCurve

section qExpandAlgC

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]

end qExpandAlgC

section FrobeniusGeomLevel

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]

theorem charP_laurentSeries_alt : CharP (LaurentSeries K) ℓ :=
  charP_of_injective_algebraMap (algebraMap K (LaurentSeries K)).injective ℓ

theorem qExpand_jqNModC_eq_pow_unconditional_def :
    qExpand K ℓ (jqNModC K N) = (jqNModC K N) ^ ℓ := by
  rw [jqNModC, qExpand_qExpand, qExpand_congr (mul_comm ℓ N), ← qExpand_qExpand,
    qExpand_jqModC_eq_pow_unconditional K, map_pow]

theorem frobeniusGeomLevel_map_le_unconditional :
    (modularFunctionFieldC K N).map (qExpandAlgC K ℓ) ≤ modularFunctionFieldC K N := by
  show (IntermediateField.adjoin K {jqModC K, jqNModC K N}).map (qExpandAlgC K ℓ) ≤ _
  rw [IntermediateField.adjoin_map, Set.image_pair, IntermediateField.adjoin_le_iff]
  rintro x (rfl | rfl)
  · show qExpandAlgC K ℓ (jqModC K) ∈ modularFunctionFieldC K N
    rw [qExpandAlgC_apply, qExpand_jqModC_eq_pow_unconditional K]
    exact pow_mem (jqModC_mem K N) ℓ
  · show qExpandAlgC K ℓ (jqNModC K N) ∈ modularFunctionFieldC K N
    rw [qExpandAlgC_apply, qExpand_jqNModC_eq_pow_unconditional_def K N]
    exact pow_mem (jqNModC_mem K N) ℓ

def frobeniusGeomLevelUnconditional :
    modularFunctionFieldC K N →ₐ[K] modularFunctionFieldC K N :=
  (IntermediateField.inclusion (frobeniusGeomLevel_map_le_unconditional K N)).comp
    (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toAlgHom

@[simp]
theorem frobeniusGeomLevelUnconditional_apply_coe (x : modularFunctionFieldC K N) :
    (frobeniusGeomLevelUnconditional K N (ℓ := ℓ) x : LaurentSeries K)
      = qExpand K ℓ (x : LaurentSeries K) := by
  refine (IntermediateField.coe_inclusion (frobeniusGeomLevel_map_le_unconditional K N)
    _).trans ?_
  exact coe_frobeniusGeomLevelEquiv_apply K N x

theorem frobeniusGeomLevelUnconditional_injective :
    Function.Injective (frobeniusGeomLevelUnconditional K N (ℓ := ℓ)) := by
  intro x y h
  have h' := congrArg (fun z : modularFunctionFieldC K N => (z : LaurentSeries K)) h
  simp only [frobeniusGeomLevelUnconditional_apply_coe] at h'
  exact Subtype.ext (qExpand_injective _ h')

theorem exists_frobeniusGeomLevelUnconditional_eq_iff (x : modularFunctionFieldC K N) :
    (∃ y, frobeniusGeomLevelUnconditional K N (ℓ := ℓ) y = x)
      ↔ (x : LaurentSeries K) ∈ frobeniusGeomLevelImage K N (ℓ := ℓ) := by
  constructor
  · rintro ⟨y, rfl⟩
    rw [frobeniusGeomLevelUnconditional_apply_coe]
    exact ⟨(y : LaurentSeries K), y.2, rfl⟩
  · rintro ⟨y, hy, hyx⟩
    refine ⟨⟨y, hy⟩, Subtype.ext ?_⟩
    rw [frobeniusGeomLevelUnconditional_apply_coe]
    exact hyx

theorem pow_mem_frobeniusGeomLevelImage_unconditional
    {x : LaurentSeries K} (hx : x ∈ modularFunctionFieldC K N) :
    x ^ ℓ ∈ frobeniusGeomLevelImage K N (ℓ := ℓ) := by
  haveI : CharP (LaurentSeries K) ℓ := charP_laurentSeries_alt K
  induction hx using IntermediateField.adjoin_induction with
  | mem y hy =>
    rcases hy with rfl | rfl
    · rw [← qExpand_jqModC_eq_pow_unconditional K]
      exact ⟨jqModC K, jqModC_mem K N, rfl⟩
    · rw [← qExpand_jqNModC_eq_pow_unconditional_def K N]
      exact ⟨jqNModC K N, jqNModC_mem K N, rfl⟩
  | algebraMap c =>
    rw [← map_pow]
    exact (frobeniusGeomLevelImage K N (ℓ := ℓ)).algebraMap_mem (c ^ ℓ)
  | add y z _ _ hy hz =>
    rw [add_pow_char]
    exact add_mem hy hz
  | inv y _ hy =>
    rw [inv_pow]
    exact inv_mem hy
  | mul y z _ _ hy hz =>
    rw [mul_pow]
    exact mul_mem hy hz

theorem exists_frobeniusGeomLevelUnconditional_eq_pow (x : modularFunctionFieldC K N) :
    ∃ y, frobeniusGeomLevelUnconditional K N (ℓ := ℓ) y = x ^ ℓ := by
  rw [exists_frobeniusGeomLevelUnconditional_eq_iff]
  show (x : LaurentSeries K) ^ ℓ ∈ frobeniusGeomLevelImage K N (ℓ := ℓ)
  exact pow_mem_frobeniusGeomLevelImage_unconditional K N x.2

end FrobeniusGeomLevel

section ValSubringRoots

variable {ℓ : ℕ} [Fact ℓ.Prime]

end ValSubringRoots

section PlaceLevel

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]

@[reducible] def frobImageAlgebraUnconditional :
    Algebra (frobeniusGeomLevelImage K N (ℓ := ℓ)) (modularFunctionFieldC K N) :=
  (IntermediateField.inclusion
    (frobeniusGeomLevel_map_le_unconditional K N)).toRingHom.toAlgebra

theorem frobImageTowerUnconditional :
    letI := frobImageAlgebraUnconditional K N (ℓ := ℓ)
    IsScalarTower K (frobeniusGeomLevelImage K N (ℓ := ℓ)) (modularFunctionFieldC K N) :=
  letI := frobImageAlgebraUnconditional K N (ℓ := ℓ)
  IsScalarTower.of_algebraMap_eq fun _ => Subtype.ext rfl

theorem frobImageIsIntegralUnconditional :
    letI := frobImageAlgebraUnconditional K N (ℓ := ℓ)
    Algebra.IsIntegral (frobeniusGeomLevelImage K N (ℓ := ℓ))
      (modularFunctionFieldC K N) := by
  letI := frobImageAlgebraUnconditional K N (ℓ := ℓ)
  refine ⟨fun x => ?_⟩
  refine ⟨Polynomial.X ^ ℓ - Polynomial.C
    ⟨(x : LaurentSeries K) ^ ℓ, pow_mem_frobeniusGeomLevelImage_unconditional K N x.2⟩,
    Polynomial.monic_X_pow_sub_C _ (Fact.out : ℓ.Prime).pos.ne', ?_⟩
  rw [Polynomial.eval₂_sub, Polynomial.eval₂_X_pow, Polynomial.eval₂_C, sub_eq_zero]
  exact Subtype.ext rfl

def frobOnPlacesGeomLevelUnconditional (w : Place K (modularFunctionFieldC K N)) :
    Place K (modularFunctionFieldC K N) :=
  letI := frobImageAlgebraUnconditional K N (ℓ := ℓ)
  letI := frobImageTowerUnconditional K N (ℓ := ℓ)
  letI := frobImageIsIntegralUnconditional K N (ℓ := ℓ)
  (Place.congrEquiv (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toRingEquiv
    (fun a => (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).commutes a)).symm
    (w.restrict (frobeniusGeomLevelImage K N (ℓ := ℓ)))

theorem mem_frobOnPlacesGeomLevelUnconditional_iff
    (w : Place K (modularFunctionFieldC K N)) (x : modularFunctionFieldC K N) :
    x ∈ (frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) w).toValuationSubring
      ↔ frobeniusGeomLevelUnconditional K N (ℓ := ℓ) x ∈ w.toValuationSubring := by
  letI := frobImageAlgebraUnconditional K N (ℓ := ℓ)
  letI := frobImageTowerUnconditional K N (ℓ := ℓ)
  letI := frobImageIsIntegralUnconditional K N (ℓ := ℓ)
  rw [show frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) w
      = (Place.congrEquiv (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toRingEquiv
          (fun a => (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).commutes a)).symm
          (w.restrict (frobeniusGeomLevelImage K N (ℓ := ℓ))) from rfl,
    Place.congrEquiv_symm_apply, Place.congrRingEquiv_toValuationSubring,
    ValuationSubring.mem_comap, RingEquiv.symm_symm]
  exact Iff.rfl

theorem frobOnPlacesGeomLevelUnconditional_injective :
    Function.Injective (frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ)) := by
  intro w w' h
  ext1
  refine SetLike.ext fun x => ?_
  obtain ⟨y, hy⟩ := exists_frobeniusGeomLevelUnconditional_eq_pow K N x
  rw [mem_valuationSubring_iff_pow_mem (ℓ := ℓ) w.toValuationSubring x, ← hy,
    ← mem_frobOnPlacesGeomLevelUnconditional_iff K N w y, h,
    mem_frobOnPlacesGeomLevelUnconditional_iff K N w' y, hy,
    ← mem_valuationSubring_iff_pow_mem (ℓ := ℓ) w'.toValuationSubring x]

open Classical in

def verOnPlacesGeomLevelUnconditional (u : Place K (modularFunctionFieldC K N)) :
    Place K (modularFunctionFieldC K N) :=
  if h : ∃ w, frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) w = u then h.choose else u

theorem verOnPlacesGeomLevelUnconditional_frobOnPlacesGeomLevelUnconditional
    (w : Place K (modularFunctionFieldC K N)) :
    verOnPlacesGeomLevelUnconditional K N (ℓ := ℓ)
      (frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) w) = w := by
  rw [verOnPlacesGeomLevelUnconditional, dif_pos ⟨w, rfl⟩]
  exact frobOnPlacesGeomLevelUnconditional_injective K N
    (Exists.choose_spec
      (⟨w, rfl⟩ : ∃ w', frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) w'
        = frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) w))

end PlaceLevel

section DivisorLevel

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]

def frobeniusPushforwardGeomLevelUnconditional :
    Divisor K (modularFunctionFieldC K N) →+ Divisor K (modularFunctionFieldC K N) :=
  Finsupp.mapDomain.addMonoidHom (frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ))

def frobeniusPullbackGeomLevelUnconditional :
    Divisor K (modularFunctionFieldC K N) →+ Divisor K (modularFunctionFieldC K N) :=
  Finsupp.liftAddHom fun v =>
    (Finsupp.singleAddHom (verOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) v)).comp
      (AddMonoidHom.mulRight (ℓ : ℤ))

def heckeFibreGeomLevelUnconditional :
    Divisor K (modularFunctionFieldC K N) →+ Divisor K (modularFunctionFieldC K N) :=
  frobeniusPushforwardGeomLevelUnconditional K N (ℓ := ℓ)
    + frobeniusPullbackGeomLevelUnconditional K N (ℓ := ℓ)

@[simp]
theorem frobeniusPushforwardGeomLevelUnconditional_single
    (w : Place K (modularFunctionFieldC K N)) (n : ℤ) :
    frobeniusPushforwardGeomLevelUnconditional K N (ℓ := ℓ) (Finsupp.single w n)
      = Finsupp.single (frobOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) w) n := by
  simp [frobeniusPushforwardGeomLevelUnconditional, Finsupp.mapDomain.addMonoidHom_apply,
    Finsupp.mapDomain_single]

@[simp]
theorem frobeniusPullbackGeomLevelUnconditional_single
    (w : Place K (modularFunctionFieldC K N)) (n : ℤ) :
    frobeniusPullbackGeomLevelUnconditional K N (ℓ := ℓ) (Finsupp.single w n)
      = Finsupp.single (verOnPlacesGeomLevelUnconditional K N (ℓ := ℓ) w) (n * ℓ) := by
  simp [frobeniusPullbackGeomLevelUnconditional]

theorem frobeniusPullbackGeomLevelUnconditional_pushforward
    (D : Divisor K (modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevelUnconditional K N (ℓ := ℓ)
        (frobeniusPushforwardGeomLevelUnconditional K N (ℓ := ℓ) D) = (ℓ : ℤ) • D := by
  induction D using Finsupp.induction with
  | zero => simp
  | single_add v n D _ _ ih =>
    rw [map_add, map_add, ih, frobeniusPushforwardGeomLevelUnconditional_single,
      frobeniusPullbackGeomLevelUnconditional_single,
      verOnPlacesGeomLevelUnconditional_frobOnPlacesGeomLevelUnconditional,
      smul_add, Finsupp.smul_single, smul_eq_mul, mul_comm]

theorem eichlerShimura_special_fibre_geom_level_unconditional
    (D : Divisor K (modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevelUnconditional K N (ℓ := ℓ)
        (frobeniusPushforwardGeomLevelUnconditional K N (ℓ := ℓ) D)
      - heckeFibreGeomLevelUnconditional K N (ℓ := ℓ)
          (frobeniusPushforwardGeomLevelUnconditional K N (ℓ := ℓ) D)
      + (ℓ : ℤ) • D = 0 := by
  rw [heckeFibreGeomLevelUnconditional, AddMonoidHom.add_apply,
    frobeniusPullbackGeomLevelUnconditional_pushforward]
  abel

end DivisorLevel

end ModularCurve
