/-
Copyright (c) 2026. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-/
import Palomar.Bridge
import «Adic spaces»

/-!
# The Challenge's finite-jet algebra is the library's

The Challenge defines `𝓐` as the closure of the jet polynomials ([FJP] (1.7)) in
`K⟨W, W⁻¹, Q⟩ = Completion K[W, W⁻¹, Q]` for the Gauss norm; the library defines it as the
support subring `jetSupport` of `L⟨Q⟩ = PowerSeries.Restricted (RestrictedLaurent K) 1`. This
file identifies them: the obvious embedding `ι : K[W, W⁻¹, Q] → L⟨Q⟩` is an isometry with
dense image, so it extends to an isometric ring isomorphism of the completion with `L⟨Q⟩`,
which carries the closure of the jet polynomials onto `jetSupport`.
-/

set_option autoImplicit false

noncomputable section

open Filter Topology

namespace PalomarBridge.Jet

open FiniteJetOver

universe u

variable (K : Type u) [NontriviallyNormedField K] [IsUltrametricDist K] [CompleteSpace K]

/-! ### Monomials `W^a Q^q` and the embedding `ι` -/

/-- The monomial `W^a Q^q` of `L⟨Q⟩`. -/
def mono (a : ℤ) (q : ℕ) : JetC K :=
  ⟨PowerSeries.monomial q (FiniteJet.RestrictedLaurent.single a (1 : K)),
    PowerSeries.IsRestricted.monomial (1 : ℝ) q _⟩

theorem mono_zero_zero : mono K 0 0 = 1 := by
  apply Subtype.ext
  show PowerSeries.monomial 0 (FiniteJet.RestrictedLaurent.single 0 (1 : K)) = 1
  rw [FiniteJet.RestrictedLaurent.single_zero_one, PowerSeries.monomial_zero_eq_C_apply, map_one]

theorem mono_mul (a b : ℤ) (q r : ℕ) : mono K a q * mono K b r = mono K (a + b) (q + r) := by
  apply Subtype.ext
  show PowerSeries.monomial q _ * PowerSeries.monomial r _ = PowerSeries.monomial (q + r) _
  rw [PowerSeries.monomial_mul_monomial, FiniteJet.RestrictedLaurent.single_mul_single, one_mul]

/-- The monomials, as a monoid hom out of `Multiplicative (ℤ × ℕ)`. -/
def monoHom : Multiplicative (ℤ × ℕ) →* JetC K where
  toFun m := mono K m.toAdd.1 m.toAdd.2
  map_one' := mono_zero_zero K
  map_mul' _ _ := (mono_mul K _ _ _ _).symm

/-- `ι : K[W, W⁻¹, Q] →+* L⟨Q⟩`, sending `c · W^a Q^q` to itself. -/
def iota : AddMonoidAlgebra K (ℤ × ℕ) →+* JetC K :=
  AddMonoidAlgebra.liftNCRingHom (constC K) (monoHom K) fun _ _ => Commute.all _ _

theorem iota_single (a : ℤ) (q : ℕ) (c : K) :
    iota K (AddMonoidAlgebra.single (a, q) c) = constC K c * mono K a q :=
  AddMonoidAlgebra.liftNC_single _ _ _ _

/-- `ι` followed by the inclusion into `PowerSeries (L K)`. -/
def iotaVal : AddMonoidAlgebra K (ℤ × ℕ) →+* PowerSeries (L K) :=
  (PowerSeries.isSubring (R := L K) (1 : ℝ)).subtype.comp (iota K)

theorem iotaVal_apply (p : AddMonoidAlgebra K (ℤ × ℕ)) : iotaVal K p = (iota K p).1 := rfl

theorem iotaVal_single (m : ℤ × ℕ) (c : K) :
    iotaVal K (AddMonoidAlgebra.single m c) =
      PowerSeries.monomial m.2 (FiniteJet.RestrictedLaurent.single m.1 c) := by
  obtain ⟨a, q⟩ := m
  rw [iotaVal_apply, iota_single]
  show PowerSeries.C (FiniteJet.RestrictedLaurent.single 0 c) *
    PowerSeries.monomial q (FiniteJet.RestrictedLaurent.single a 1) = _
  rw [← PowerSeries.monomial_zero_eq_C_apply, PowerSeries.monomial_mul_monomial, zero_add,
    FiniteJet.RestrictedLaurent.single_mul_single, zero_add, mul_one]

/-- The `Q^n`-coefficient of `ι p` is the Laurent series `∑ₐ p(a, n) W^a`. -/
theorem coeff_iota (p : AddMonoidAlgebra K (ℤ × ℕ)) (n : ℕ) (a : ℤ) :
    (PowerSeries.coeff n (iota K p).1).coeff a = p.coeff (a, n) := by
  classical
  rw [← iotaVal_apply]
  conv_lhs => rw [← AddMonoidAlgebra.sum_coeff_single p, map_finsuppSum, map_finsuppSum]
  simp only [iotaVal_single, PowerSeries.coeff_monomial]
  rw [show (p.coeff.sum fun m c =>
      if n = m.2 then FiniteJet.RestrictedLaurent.single m.1 c else 0).coeff a =
      p.coeff.sum fun m c => if m = (a, n) then c else 0 from ?_]
  · rw [Finsupp.sum_ite_eq']
    split_ifs with h
    · rfl
    · exact (Finsupp.notMem_support_iff.mp h).symm
  · rw [Finsupp.sum, Finsupp.sum]
    have hcoe : ∀ (s : Finset (ℤ × ℕ)) (g : ℤ × ℕ → FiniteJet.RestrictedLaurent K),
        (∑ m ∈ s, g m).coeff a = ∑ m ∈ s, (g m).coeff a := fun s g =>
      map_sum (FiniteJet.RestrictedLaurent.coeffHom a) g s
    rw [hcoe]
    refine Finset.sum_congr rfl fun m _ => ?_
    by_cases h2 : n = m.2
    · by_cases h1 : a = m.1
      · rw [if_pos h2, FiniteJet.RestrictedLaurent.coeff_single, if_pos h1,
          if_pos (Prod.ext h1.symm h2.symm)]
      · rw [if_pos h2, FiniteJet.RestrictedLaurent.coeff_single, if_neg h1,
          if_neg (fun h => h1 (congrArg Prod.fst h).symm)]
    · rw [if_neg h2, FiniteJet.RestrictedLaurent.coeff_zero,
        if_neg (fun h => h2 (congrArg Prod.snd h).symm)]

/-! ### `ι` is an isometry -/

/-- The Gauss norm of `L⟨Q⟩`, coefficient by coefficient. -/
theorem norm_jetC_eq (g : JetC K) :
    ‖g‖ = ⨆ n : ℕ, ⨆ a : ℤ, ‖(PowerSeries.coeff n g.1).coeff a‖ := by
  rw [Restricted.norm_eq, PowerSeries.gaussNorm_eq]
  simp only [one_pow, mul_one]
  rfl

theorem norm_coeff_coeff_le (g : JetC K) (n : ℕ) (a : ℤ) :
    ‖(PowerSeries.coeff n g.1).coeff a‖ ≤ ‖g‖ := by
  rw [norm_jetC_eq]
  have hbdd : ∀ n : ℕ, BddAbove (Set.range fun a : ℤ => ‖(PowerSeries.coeff n g.1).coeff a‖) :=
    fun n => ⟨‖PowerSeries.coeff n g.1‖, by
      rintro _ ⟨a, rfl⟩
      exact FiniteJet.RestrictedLaurent.norm_coeff_le_gaussNorm _ a⟩
  have hbdd' : BddAbove (Set.range fun n : ℕ =>
      ⨆ a : ℤ, ‖(PowerSeries.coeff n g.1).coeff a‖) := ⟨‖g‖, by
    rintro _ ⟨n, rfl⟩
    refine ciSup_le fun a => ?_
    calc ‖(PowerSeries.coeff n g.1).coeff a‖ ≤ ‖PowerSeries.coeff n g.1‖ :=
          FiniteJet.RestrictedLaurent.norm_coeff_le_gaussNorm _ a
      _ ≤ ‖g‖ := by
        rw [Restricted.norm_eq]
        simpa using PowerSeries.le_gaussNorm norm 1 g.1 (Restricted.hasGaussNorm 1 g) n⟩
  exact le_ciSup_of_le hbdd' n (le_ciSup (hbdd n) a)

theorem norm_coeff_le_norm (p : AddMonoidAlgebra K (ℤ × ℕ)) (m : ℤ × ℕ) :
    ‖p.coeff m‖ ≤ ‖p‖ := by
  rw [← coe_nnnorm, ← coe_nnnorm, Palomar.nnnorm_eq_gauss]
  exact_mod_cast Palomar.nnnorm_coeff_le_gauss p m

theorem norm_iota (p : AddMonoidAlgebra K (ℤ × ℕ)) : ‖iota K p‖ = ‖p‖ := by
  apply le_antisymm
  · rw [norm_jetC_eq]
    refine Real.iSup_le (fun n => Real.iSup_le (fun a => ?_) (norm_nonneg _)) (norm_nonneg _)
    rw [coeff_iota]
    exact norm_coeff_le_norm K p (a, n)
  · by_cases hp : p = 0
    · subst hp; simp
    · have hne : p.coeff.support.Nonempty := Finsupp.support_nonempty_iff.mpr fun h =>
        hp (AddMonoidAlgebra.coeff_injective (h.trans AddMonoidAlgebra.coeff_zero.symm))
      obtain ⟨m₀, -, hsup⟩ := Finset.exists_mem_eq_sup _ hne fun m => ‖p.coeff m‖₊
      have h0 : ‖p‖ = ‖p.coeff m₀‖ := by
        rw [← coe_nnnorm, Palomar.nnnorm_eq_gauss, Palomar.gauss, hsup, coe_nnnorm]
      rw [h0, ← coeff_iota K p m₀.2 m₀.1]
      exact norm_coeff_coeff_le K (iota K p) m₀.2 m₀.1

/-! ### `ι` has dense range -/

/-- The truncation of `g ∈ L⟨Q⟩` to the box `|a| ≤ N`, `n ≤ N`, as a Laurent polynomial. -/
def trunc (N : ℕ) (g : JetC K) : AddMonoidAlgebra K (ℤ × ℕ) :=
  .ofCoeff <| Finsupp.onFinset (Finset.Icc (-(N : ℤ)) N ×ˢ Finset.range (N + 1))
    (fun m => if |m.1| ≤ N ∧ m.2 ≤ N then (PowerSeries.coeff m.2 g.1).coeff m.1 else 0)
    fun m hm => by
      by_contra hbox
      refine hm (if_neg fun h => hbox ?_)
      rw [Finset.mem_product, Finset.mem_Icc, Finset.mem_range, abs_le] at *
      exact ⟨⟨h.1.1, h.1.2⟩, by omega⟩

theorem coeff_trunc (N : ℕ) (g : JetC K) (m : ℤ × ℕ) :
    (trunc K N g).coeff m =
      if |m.1| ≤ N ∧ m.2 ≤ N then (PowerSeries.coeff m.2 g.1).coeff m.1 else 0 := rfl

/-- The coefficients of `g ∈ L⟨Q⟩` tend to zero jointly in `(a, n)`: outside a large enough
box they are all small. -/
theorem exists_box (g : JetC K) {ε : ℝ} (hε : 0 < ε) :
    ∃ N : ℕ, ∀ (a : ℤ) (n : ℕ), ¬ (|a| ≤ N ∧ n ≤ N) →
      ‖(PowerSeries.coeff n g.1).coeff a‖ < ε := by
  -- the `Q`-coefficients tend to zero in `L`
  have h1 : Tendsto (fun n => ‖PowerSeries.coeff n g.1‖) atTop (𝓝 0) := by
    have h0 : PowerSeries.IsRestricted (1 : ℝ) g.1 := g.2
    unfold PowerSeries.IsRestricted at h0
    simpa only [one_pow, mul_one] using h0
  obtain ⟨N₁, hN₁⟩ := Metric.tendsto_atTop.mp h1 ε hε
  -- for each of the finitely many `n < N₁`, finitely many `W`-coefficients are large
  have hfin : ∀ n : ℕ, {a : ℤ | ε ≤ ‖(PowerSeries.coeff n g.1).coeff a‖}.Finite := fun n =>
    FiniteJet.RestrictedLaurent.finite_setOf_le_norm_coeff _ hε
  classical
  let S : Finset ℤ := (Finset.range N₁).biUnion fun n => (hfin n).toFinset
  refine ⟨max N₁ (S.sup fun a => a.natAbs), fun a n hbox => ?_⟩
  by_cases hn : N₁ ≤ n
  · -- `n` large: the whole `Q^n`-coefficient is small
    have := hN₁ n hn
    rw [Real.dist_eq, sub_zero, abs_of_nonneg (norm_nonneg _)] at this
    exact (FiniteJet.RestrictedLaurent.norm_coeff_le_gaussNorm _ a).trans_lt this
  · -- `n` small, so `|a|` is large: `a` is outside the finite set of large coefficients
    push Not at hn
    by_contra hle
    push Not at hle
    have haS : a ∈ S := Finset.mem_biUnion.mpr ⟨n, Finset.mem_range.mpr hn,
      (hfin n).mem_toFinset.mpr hle⟩
    have h1 : a.natAbs ≤ S.sup fun a => a.natAbs := Finset.le_sup (f := fun a : ℤ => a.natAbs) haS
    refine hbox ⟨?_, by omega⟩
    rw [Int.abs_eq_natAbs]
    exact_mod_cast h1.trans (le_max_right _ _)

theorem exists_norm_sub_iota_trunc_le (g : JetC K) {ε : ℝ} (hε : 0 < ε) :
    ∃ N : ℕ, ‖g - iota K (trunc K N g)‖ ≤ ε := by
  obtain ⟨N, hN⟩ := exists_box K g hε
  refine ⟨N, ?_⟩
  rw [norm_jetC_eq]
  refine Real.iSup_le (fun n => Real.iSup_le (fun a => ?_) hε.le) hε.le
  have hc : (PowerSeries.coeff n (g - iota K (trunc K N g)).1).coeff a =
      (PowerSeries.coeff n g.1).coeff a - (trunc K N g).coeff (a, n) := by
    rw [show (g - iota K (trunc K N g)).1 = g.1 - (iota K (trunc K N g)).1 from rfl, map_sub,
      FiniteJet.RestrictedLaurent.coeff_sub, coeff_iota]
  rw [hc, coeff_trunc]
  split_ifs with h
  · simp [hε.le]
  · rw [sub_zero]; exact (hN a n h).le

theorem denseRange_iota : DenseRange (iota K) := by
  refine Metric.denseRange_iff.mpr fun g ε hε => ?_
  obtain ⟨N, hN⟩ := exists_norm_sub_iota_trunc_le K g (half_pos hε)
  exact ⟨trunc K N g, by rw [dist_eq_norm]; exact hN.trans_lt (half_lt_self hε)⟩

theorem isometry_iota : Isometry (iota K) :=
  AddMonoidHomClass.isometry_of_norm (iota K) (norm_iota K)

/-! ### The completion of `K[W, W⁻¹, Q]` is `L⟨Q⟩` -/

/-- `L⟨Q⟩`, with `ι`, is an abstract completion of `K[W, W⁻¹, Q]`. -/
def jetCPkg : AbstractCompletion (AddMonoidAlgebra K (ℤ × ℕ)) where
  space := JetC K
  coe := iota K
  uniformStruct := inferInstance
  complete := inferInstance
  separation := inferInstance
  isUniformInducing := (isometry_iota K).isUniformInducing
  dense := denseRange_iota K

/-- The uniform isomorphism `K⟨W, W⁻¹, Q⟩ ≃ᵤ L⟨Q⟩`. -/
def jetCUniformEquiv : Palomar.TateAlgebra K (ℤ × ℕ) ≃ᵤ JetC K :=
  UniformSpace.Completion.cPkg.compareEquiv (jetCPkg K)

theorem jetCUniformEquiv_apply (x : Palomar.TateAlgebra K (ℤ × ℕ)) :
    jetCUniformEquiv K x = UniformSpace.Completion.extension (iota K) x := rfl

/-- **`K⟨W, W⁻¹, Q⟩ ≃+* L⟨Q⟩`**: the completion of the Laurent polynomials for the Gauss norm is
the restricted series ring. -/
def jetCEquiv : Palomar.TateAlgebra K (ℤ × ℕ) ≃+* JetC K :=
  { UniformSpace.Completion.extensionHom (iota K) (isometry_iota K).continuous with
    invFun := (jetCUniformEquiv K).symm
    left_inv := fun x => (jetCUniformEquiv K).symm_apply_apply x
    right_inv := fun y => (jetCUniformEquiv K).apply_symm_apply y }

theorem jetCEquiv_coe (p : AddMonoidAlgebra K (ℤ × ℕ)) :
    jetCEquiv K (p : Palomar.TateAlgebra K (ℤ × ℕ)) = iota K p :=
  UniformSpace.Completion.extensionHom_coe (iota K) (isometry_iota K).continuous p

theorem continuous_jetCEquiv : Continuous (jetCEquiv K) :=
  UniformSpace.Completion.continuous_extension

theorem norm_jetCEquiv (x : Palomar.TateAlgebra K (ℤ × ℕ)) : ‖jetCEquiv K x‖ = ‖x‖ := by
  induction x using UniformSpace.Completion.induction_on with
  | hp => exact isClosed_eq (continuous_norm.comp (continuous_jetCEquiv K)) continuous_norm
  | ih p => rw [jetCEquiv_coe, norm_iota, UniformSpace.Completion.norm_coe]

theorem continuous_jetCEquiv_symm : Continuous (jetCEquiv K).symm :=
  (jetCUniformEquiv K).symm.continuous

/-! ### The jet subrings correspond -/

/-- Jet polynomials land in the library's support subring. -/
theorem map_jetPolys_le : (Palomar.jetPolys K).map (iota K) ≤ jetSupport K := by
  rintro _ ⟨p, hp, rfl⟩
  refine ⟨fun a ha => ?_, fun a ha => ?_⟩
  · show (PowerSeries.coeff 0 (iota K p).1).coeff a = 0
    rw [coeff_iota]; exact hp a 0 zero_le_one ha
  · show (PowerSeries.coeff 1 (iota K p).1).coeff a = 0
    rw [coeff_iota]; exact hp a 1 le_rfl ha

/-- Truncating an element of the support subring gives a jet polynomial. -/
theorem trunc_mem_jetPolys (N : ℕ) {g : JetC K} (hg : g ∈ jetSupport K) :
    trunc K N g ∈ Palomar.jetPolys K := by
  intro a q hq ha
  rw [coeff_trunc]
  split_ifs with h
  · interval_cases q
    · exact hg.1 a ha
    · exact hg.2 a ha
  · rfl

/-- The closure of the jet polynomials in `L⟨Q⟩` is exactly the support subring. -/
theorem topologicalClosure_map_jetPolys :
    ((Palomar.jetPolys K).map (iota K)).topologicalClosure = jetSupport K := by
  refine le_antisymm (Subring.topologicalClosure_minimal _ (map_jetPolys_le K)
    (isClosed_jetSupport K)) fun g hg => ?_
  show g ∈ closure ((Palomar.jetPolys K).map (iota K) : Set (JetC K))
  refine Metric.mem_closure_iff.mpr fun ε hε => ?_
  obtain ⟨N, hN⟩ := exists_norm_sub_iota_trunc_le K g (half_pos hε)
  exact ⟨iota K (trunc K N g), ⟨trunc K N g, trunc_mem_jetPolys K N hg, rfl⟩,
    by rw [dist_eq_norm]; exact hN.trans_lt (half_lt_self hε)⟩

/-- The completion isomorphism carries the Challenge's `𝓐` onto the library's. -/
theorem map_jetSubring :
    (Palomar.jetSubring K).map (jetCEquiv K).toRingHom = jetSupport K := by
  rw [← topologicalClosure_map_jetPolys]
  apply SetLike.ext'
  -- both sides are images/closures of the jet polynomials
  show (jetCEquiv K) '' closure (((Palomar.jetPolys K).map
      (UniformSpace.Completion.coeRingHom : AddMonoidAlgebra K (ℤ × ℕ) →+*
        Palomar.TateAlgebra K (ℤ × ℕ)) : Subring _) : Set _) =
    closure (((Palomar.jetPolys K).map (iota K) : Subring _) : Set _)
  have he : (⇑(jetCEquiv K) : Palomar.TateAlgebra K (ℤ × ℕ) → JetC K) =
      ⇑(jetCUniformEquiv K).toHomeomorph := rfl
  rw [he, Homeomorph.image_closure, ← he]
  congr 1
  show (jetCEquiv K) '' ((UniformSpace.Completion.coeRingHom : AddMonoidAlgebra K (ℤ × ℕ) →+*
      Palomar.TateAlgebra K (ℤ × ℕ)) '' (Palomar.jetPolys K : Set _)) =
    (iota K) '' (Palomar.jetPolys K : Set _)
  rw [Set.image_image]
  exact Set.image_congr fun p _ => jetCEquiv_coe K p

/-- **The Challenge's `𝓐` is the library's `𝓐`**, as rings. -/
def jetAEquiv : Palomar.JetA K ≃+* JetA K :=
  (RingEquiv.subringMap (jetCEquiv K) (s := Palomar.jetSubring K)).trans
    (RingEquiv.subringCongr (map_jetSubring K))

theorem coe_jetAEquiv (x : Palomar.JetA K) : (jetAEquiv K x : JetC K) = jetCEquiv K x := rfl

theorem coe_jetAEquiv_symm (y : JetA K) :
    (((jetAEquiv K).symm y : Palomar.JetA K) : Palomar.TateAlgebra K (ℤ × ℕ)) =
      (jetCEquiv K).symm y := by
  apply (jetCEquiv K).injective
  rw [RingEquiv.apply_symm_apply, ← coe_jetAEquiv, RingEquiv.apply_symm_apply]

/-- The identification is isometric. -/
theorem norm_jetAEquiv (x : Palomar.JetA K) : ‖jetAEquiv K x‖ = ‖x‖ := by
  show ‖(jetAEquiv K x : JetC K)‖ = ‖(x : Palomar.TateAlgebra K (ℤ × ℕ))‖
  rw [coe_jetAEquiv, norm_jetCEquiv]

theorem norm_jetAEquiv_symm (y : JetA K) : ‖(jetAEquiv K).symm y‖ = ‖y‖ := by
  rw [← norm_jetAEquiv, RingEquiv.apply_symm_apply]

theorem isometry_jetAEquiv : Isometry (jetAEquiv K) :=
  AddMonoidHomClass.isometry_of_norm _ (norm_jetAEquiv K)

theorem isometry_jetAEquiv_symm : Isometry (jetAEquiv K).symm :=
  AddMonoidHomClass.isometry_of_norm _ (norm_jetAEquiv_symm K)

theorem continuous_jetAEquiv : Continuous (jetAEquiv K) := (isometry_jetAEquiv K).continuous

theorem continuous_jetAEquiv_symm : Continuous (jetAEquiv K).symm :=
  (isometry_jetAEquiv_symm K).continuous

/-- The identification as a homeomorphism. -/
def jetAHomeo : Palomar.JetA K ≃ₜ JetA K where
  toEquiv := (jetAEquiv K).toEquiv
  continuous_toFun := continuous_jetAEquiv K
  continuous_invFun := continuous_jetAEquiv_symm K

end PalomarBridge.Jet
