/-
Copyright (c) 2026 Michael R. Douglas. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

# Parametric Calculus

General-purpose lemmas for iterated Fréchet derivatives and parametric integrals,
not yet available in Mathlib. Used as building blocks in the SchwartzNuclear proofs.

## Main results

- `norm_iteratedFDeriv_iteratedFDeriv` — **Currying isometry**: composing two levels of
  iterated derivatives preserves norms.
- `contDiff_schwartz_parametric_integral` — **Leibniz integral rule (C^∞)**: if a Schwartz
  function is integrated against another Schwartz function along a smooth parametric embedding,
  the result is C^∞ and derivatives commute with the integral.
-/

import Mathlib.Analysis.Distribution.SchwartzSpace.Basic
import Mathlib.Analysis.Calculus.ContDiff.FTaylorSeries
import Mathlib.Analysis.Calculus.ParametricIntegral

open MeasureTheory SchwartzMap
open scoped ContDiff

/-! ### Helper: currying isometry with NormedAddCommGroup instances

The `continuousMultilinearCurryLeftEquiv` is parameterized by `SeminormedAddCommGroup`
instances, while `LinearIsometryEquiv.norm_iteratedFDeriv_comp_left` requires
`NormedAddCommGroup`. We rebuild the isometry with explicit `NormedAddCommGroup`
instances to resolve this diamond. -/

private noncomputable def curryLeftLIE {E F : Type*}
    [NormedAddCommGroup E] [NormedSpace ℝ E]
    [NormedAddCommGroup F] [NormedSpace ℝ F] (k : ℕ) :=
  let L := (continuousMultilinearCurryLeftEquiv ℝ (fun _ : Fin (k + 1) => E) F).symm
  @LinearIsometryEquiv.mk ℝ ℝ _ _ (RingHom.id ℝ) (RingHom.id ℝ) _ _
    (E →L[ℝ] ContinuousMultilinearMap ℝ (fun _ : Fin k => E) F)
    (ContinuousMultilinearMap ℝ (fun _ : Fin (k + 1) => E) F)
    NormedAddCommGroup.toSeminormedAddCommGroup
    NormedAddCommGroup.toSeminormedAddCommGroup
    NormedSpace.toModule NormedSpace.toModule
    L.toLinearEquiv L.norm_map'

private lemma curryLeftLIE_coe_eq {E F : Type*}
    [NormedAddCommGroup E] [NormedSpace ℝ E]
    [NormedAddCommGroup F] [NormedSpace ℝ F] (k : ℕ) :
    ⇑(curryLeftLIE (E := E) (F := F) k) =
    ⇑(continuousMultilinearCurryLeftEquiv ℝ (fun _ : Fin (k + 1) => E) F).symm := rfl

/-- **Textbook: Currying isometry for iterated Fréchet derivatives.**

`‖iteratedFDeriv n (iteratedFDeriv l' f) z‖ = ‖iteratedFDeriv (n + l') f z‖`

Proof by induction on `l'`. The key difficulty is a typeclass diamond: the
`SeminormedAddCommGroup` instances on `ContinuousMultilinearMap` and `ContinuousLinearMap`
differ from those derived from `NormedAddCommGroup`, preventing direct use of
`LinearIsometryEquiv.norm_iteratedFDeriv_comp_left`. We resolve this by rebuilding the
currying isometry with explicit `NormedAddCommGroup` instances via `curryLeftLIE`. -/
lemma norm_iteratedFDeriv_iteratedFDeriv {E F : Type*}
    [NormedAddCommGroup E] [NormedSpace ℝ E]
    [NormedAddCommGroup F] [NormedSpace ℝ F]
    (f : E → F) (n l' : ℕ) (z : E) :
    ‖iteratedFDeriv ℝ n (iteratedFDeriv ℝ l' f) z‖ =
      ‖iteratedFDeriv ℝ (n + l') f z‖ := by
  revert n
  induction l' with
  | zero =>
    intro n
    simp only [Nat.add_zero]
    rw [iteratedFDeriv_zero_eq_comp]
    exact (continuousMultilinearCurryFin0 ℝ E F).symm.norm_iteratedFDeriv_comp_left f z n
  | succ k ih =>
    intro n
    rw [iteratedFDeriv_succ_eq_comp_left (f := f) (n := k)]
    rw [show ⇑(continuousMultilinearCurryLeftEquiv ℝ (fun _ : Fin (k + 1) => E) F).symm =
      ⇑(curryLeftLIE (E := E) (F := F) k) from (curryLeftLIE_coe_eq k).symm]
    rw [(curryLeftLIE k).norm_iteratedFDeriv_comp_left]
    rw [norm_iteratedFDeriv_fderiv]
    rw [ih (n + 1)]
    conv_lhs => rw [show n + 1 + k = n + (k + 1) from by omega]

/-! ### Leibniz integral rule for Schwartz integrands

The proof uses a "double induction" on `I n y := ∫ t, D^n_y[Φ_t(t)](y)`:
1. `I_diff`: each `I n` is differentiable with `fderiv = (curryLeftLIE n).symm ∘ I(n+1)`
2. `I_contDiff`: `ContDiff m (I n)` by induction on `m`, shifting `n`
3. `h_iter`: `iteratedFDeriv m Φ = I m` by induction on `m`

The `hι_meas` hypothesis provides measurability of the parametric iterated
derivatives `t ↦ D^m_y[f(ι(·,t))](y)`. In all applications (e.g., `ι = euclideanSnoc`),
this follows from joint smoothness of `ι` via `Continuous.aestronglyMeasurable`. -/

/-- **Leibniz integral rule (C^∞, Schwartz integrand).**

Given a Schwartz function `f` on `E`, a Schwartz function `g` on `ℝ`, and
a parametric embedding `ι : H → ℝ → E`, if:
- for each `t`, `y ↦ ι(y,t)` is C^∞,
- for each `m`, `t ↦ D^m_y[f(ι(·,t))](y)` is ae-strongly-measurable, and
- for each derivative order `m`, `‖D^m_y[f(ι(·,t))](y)‖` is uniformly bounded,

then `y ↦ ∫_t f(ι(y,t)) · g(t) dt` is C^∞ and `D^m` commutes with `∫`.

**Reference**: Folland, "Real Analysis", Theorem 2.27 (iterated version). -/
lemma contDiff_schwartz_parametric_integral
    {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [MeasurableSpace E]
    [BorelSpace E] [FiniteDimensional ℝ E]
    {H : Type*} [NormedAddCommGroup H] [NormedSpace ℝ H]
    (f : SchwartzMap E ℝ) (g : SchwartzMap ℝ ℝ)
    (ι : H → ℝ → E)
    (hι_smooth : ∀ t, ContDiff ℝ ∞ (ι · t))
    (hι_meas : ∀ (m : ℕ) (y : H), AEStronglyMeasurable
      (fun t => iteratedFDeriv ℝ m (fun y' => f (ι y' t)) y) volume)
    (hι_bound : ∀ m : ℕ, ∃ C : ℝ, ∀ y t,
      ‖iteratedFDeriv ℝ m (fun y' => f (ι y' t)) y‖ ≤ C) :
    ContDiff ℝ ∞ (fun y => ∫ t, f (ι y t) * g t) ∧
    ∀ (m : ℕ) (y : H),
      iteratedFDeriv ℝ m (fun y' => ∫ t, f (ι y' t) * g t) y =
      ∫ t, iteratedFDeriv ℝ m (fun y' => f (ι y' t) * g t) y := by
  -- Abbreviations
  set Φ_t : ℝ → H → ℝ := fun t y => f (ι y t) * g t with hΦ_t_def
  set Φ : H → ℝ := fun y => ∫ t, Φ_t t y with hΦ_def
  -- Each inner function y ↦ f(ι(y,t)) is C^∞
  have inner_smooth : ∀ t, ContDiff ℝ ∞ (fun y => f (ι y t)) :=
    fun t => (f.smooth ⊤).comp (hι_smooth t)
  -- Each integrand y ↦ Φ_t(t,y) is C^∞ in y
  have Φ_t_smooth : ∀ t, ContDiff ℝ ∞ (Φ_t t) := by
    intro t; exact (inner_smooth t).mul contDiff_const
  -- Factorization: iteratedFDeriv m (Φ_t t) y = g t • iteratedFDeriv m (f ∘ ι · t) y
  have hΦ_t_deriv : ∀ (m : ℕ) (y : H) (t : ℝ),
      iteratedFDeriv ℝ m (Φ_t t) y =
      g t • iteratedFDeriv ℝ m (fun y' => f (ι y' t)) y := by
    intro m y t
    have h_eq : Φ_t t = fun y' => g t • f (ι y' t) := by
      ext y'; simp [Φ_t, mul_comm, smul_eq_mul]
    rw [h_eq]
    exact iteratedFDeriv_const_smul_apply'
      ((inner_smooth t).of_le (by exact_mod_cast le_top) |>.contDiffAt)
  -- Integrand bound: ‖iteratedFDeriv m (Φ_t t) y‖ ≤ C * ‖g t‖
  have h_bound : ∀ m : ℕ, ∃ C : ℝ, 0 ≤ C ∧ ∀ y t,
      ‖iteratedFDeriv ℝ m (Φ_t t) y‖ ≤ C * ‖g t‖ := by
    intro m
    obtain ⟨C, hC⟩ := hι_bound m
    refine ⟨max C 0, le_max_right _ _, fun y t => ?_⟩
    rw [hΦ_t_deriv, norm_smul (α := ℝ)]
    calc ‖g t‖ * ‖iteratedFDeriv ℝ m (fun y ↦ f (ι y t)) y‖
        ≤ ‖g t‖ * max C 0 :=
          mul_le_mul_of_nonneg_left
            (le_trans (hC y t) (le_max_left C 0)) (norm_nonneg _)
      _ = max C 0 * ‖g t‖ := mul_comm _ _
  -- g is integrable
  have g_integrable : Integrable (fun t => g t) := g.integrable
  -- Measurability of integrands at each derivative order
  have Φ_t_meas : ∀ (m : ℕ) (y : H),
      AEStronglyMeasurable (fun t => iteratedFDeriv ℝ m (Φ_t t) y) volume := by
    intro m y
    simp_rw [hΦ_t_deriv m y]
    exact g.continuous.aestronglyMeasurable.smul (hι_meas m y)
  -- Raw integrability of Φ_t t y as a function of t (ℝ-valued)
  have Φ_t_raw_integrable : ∀ (y : H), Integrable (fun t => Φ_t t y) volume := by
    intro y
    obtain ⟨C, hC_pos, hC⟩ := h_bound 0
    have hC' : ∀ y t, ‖Φ_t t y‖ ≤ C * ‖g t‖ := by
      intro y t
      have h := hC y t
      simp only [iteratedFDeriv_zero_eq_comp, Function.comp_def,
        LinearIsometryEquiv.norm_map] at h
      exact h
    exact (g_integrable.norm.const_mul C).mono
      (by -- measurability of Φ_t · y
        have h := Φ_t_meas 0 y
        simp only [iteratedFDeriv_zero_eq_comp, Function.comp_def] at h
        exact ((continuousMultilinearCurryFin0 ℝ H ℝ).symm.isometry.isEmbedding
          |>.aestronglyMeasurable_comp_iff).mp h)
      (Filter.Eventually.of_forall fun t => le_trans (hC' y t)
        (le_of_eq (Real.norm_of_nonneg (mul_nonneg hC_pos (norm_nonneg _))).symm))
  -- Integrability of iteratedFDeriv m (Φ_t ·) y as a function of t (CML-valued)
  have Φ_t_integrable : ∀ (m : ℕ) (y : H),
      Integrable (fun t => iteratedFDeriv ℝ m (Φ_t t) y) volume := by
    intro m y
    obtain ⟨C, hC_pos, hC⟩ := h_bound m
    exact (g_integrable.norm.const_mul C).mono (Φ_t_meas m y)
      (Filter.Eventually.of_forall fun t => le_trans (hC y t)
        (le_of_eq (Real.norm_of_nonneg (mul_nonneg hC_pos (norm_nonneg _))).symm))
  -- Define I n y := ∫ t, iteratedFDeriv n (Φ_t t) y
  set I : (n : ℕ) → H → ContinuousMultilinearMap ℝ (fun _ : Fin n => H) ℝ :=
    fun n y => ∫ t, iteratedFDeriv ℝ n (Φ_t t) y with hI_def
  -- Step 1: HasFDerivAt (I n) at each point
  -- fderiv(iteratedFDeriv n (Φ_t t)) y = (curryLeftLIE n).symm (iteratedFDeriv (n+1) (Φ_t t) y)
  -- so hasFDerivAt_integral gives HasFDerivAt (I n) ((curryLeftLIE n).symm (I (n+1) y)) y
  have I_diff : ∀ (n : ℕ) (y : H),
      HasFDerivAt (I n) ((curryLeftLIE (E := H) (F := ℝ) n).symm.toContinuousLinearEquiv.toContinuousLinearMap
        (I (n + 1) y)) y := by
    intro n y₀
    set L := (curryLeftLIE (E := H) (F := ℝ) n).symm.toContinuousLinearEquiv
    -- Each y ↦ iteratedFDeriv n (Φ_t t) y is differentiable (Φ_t t is C^∞)
    have h_cdiff : ∀ t, ContDiff ℝ (↑(n + 1) : ℕ∞) (Φ_t t) :=
      fun t => (Φ_t_smooth t).of_le (by exact_mod_cast le_top)
    have h_differentiable : ∀ t, Differentiable ℝ (fun y => iteratedFDeriv ℝ n (Φ_t t) y) :=
      fun t => (h_cdiff t).differentiable_iteratedFDeriv (by
        apply WithTop.coe_lt_coe.mpr
        apply WithTop.coe_lt_coe.mpr
        omega)
    -- The fderiv of iteratedFDeriv n equals L applied to iteratedFDeriv (n+1)
    -- From iteratedFDeriv_succ_eq_comp_left and curryLeftLIE_coe_eq
    have h_fderiv_eq : ∀ t y, fderiv ℝ (fun y' => iteratedFDeriv ℝ n (Φ_t t) y') y =
        L.toContinuousLinearMap (iteratedFDeriv ℝ (n + 1) (Φ_t t) y) := by
      intro t y
      have h1 := congrFun (iteratedFDeriv_succ_eq_comp_left (𝕜 := ℝ) (f := Φ_t t) (n := n)) y
      -- h1 : iteratedFDeriv (n+1) (Φ_t t) y = curryLeftEquiv.symm (fderiv (iteratedFDeriv n (Φ_t t)) y)
      rw [Function.comp_def] at h1
      -- curryLeftEquiv.symm = curryLeftLIE n as functions (by curryLeftLIE_coe_eq)
      -- So iteratedFDeriv (n+1) y = curryLeftLIE n (fderiv(iteratedFDeriv n) y)
      -- Apply (curryLeftLIE n).symm = L to both sides
      show fderiv ℝ (fun y' => iteratedFDeriv ℝ n (Φ_t t) y') y =
        (curryLeftLIE (E := H) (F := ℝ) n).symm (iteratedFDeriv ℝ (n + 1) (Φ_t t) y)
      rw [← curryLeftLIE_coe_eq] at h1
      rw [h1, (curryLeftLIE (E := H) (F := ℝ) n).symm_apply_apply]
    -- HasFDerivAt for each integrand
    have h_hasFDerivAt : ∀ t y, HasFDerivAt (fun y' => iteratedFDeriv ℝ n (Φ_t t) y')
        (L.toContinuousLinearMap (iteratedFDeriv ℝ (n + 1) (Φ_t t) y)) y := by
      intro t y
      rw [← h_fderiv_eq]
      exact (h_differentiable t y).hasFDerivAt
    -- Bound on the derivative norm: ‖L (iteratedFDeriv (n+1) ...)‖ = ‖iteratedFDeriv (n+1) ...‖ ≤ C * ‖g t‖
    obtain ⟨C, hC_pos, hC_bound⟩ := h_bound (n + 1)
    -- Apply hasFDerivAt_integral_of_dominated_of_fderiv_le
    have h_result : HasFDerivAt (fun y => ∫ t, iteratedFDeriv ℝ n (Φ_t t) y)
        (∫ t, L.toContinuousLinearMap (iteratedFDeriv ℝ (n + 1) (Φ_t t) y₀)) y₀ :=
      hasFDerivAt_integral_of_dominated_of_fderiv_le
        (F' := fun y t => L.toContinuousLinearMap (iteratedFDeriv ℝ (n + 1) (Φ_t t) y))
        (s := Set.univ) Filter.univ_mem
        (Filter.Eventually.of_forall fun x => Φ_t_meas n x)
        (Φ_t_integrable n y₀)
        (L.toContinuousLinearMap.continuous.comp_aestronglyMeasurable (Φ_t_meas (n + 1) y₀))
        (Filter.Eventually.of_forall fun t => fun y _ => by
          show ‖(curryLeftLIE (E := H) (F := ℝ) n).symm
            (iteratedFDeriv ℝ (n + 1) (Φ_t t) y)‖ ≤ C * ‖g t‖
          rw [(curryLeftLIE (E := H) (F := ℝ) n).symm.norm_map]
          exact hC_bound y t)
        (g_integrable.norm.const_mul C)
        (Filter.Eventually.of_forall fun t => fun y _ => h_hasFDerivAt t y)
    -- Commute L with the integral to get L (I (n+1) y₀)
    rw [show (∫ t, L.toContinuousLinearMap (iteratedFDeriv ℝ (n + 1) (Φ_t t) y₀)) =
      L.toContinuousLinearMap (I (n + 1) y₀) from
        L.toContinuousLinearMap.integral_comp_comm (Φ_t_integrable (n + 1) y₀)] at h_result
    exact h_result
  -- Step 2: ContDiff m (I n) for all m, n — by induction on m, shifting n
  have I_contDiff : ∀ (m : ℕ) (n : ℕ), ContDiff ℝ (m : ℕ∞) (I n) := by
    intro m
    induction m with
    | zero =>
      intro n
      have h_diff : Differentiable ℝ (I n) := fun y => (I_diff n y).differentiableAt
      exact contDiff_zero.mpr h_diff.continuous
    | succ m ih =>
      intro n
      show ContDiff ℝ ((↑↑m : WithTop ℕ∞) + 1) (I n)
      rw [contDiff_succ_iff_fderiv]
      refine ⟨fun y => (I_diff n y).differentiableAt, ?_, ?_⟩
      · -- Analyticity: vacuous since (m : ℕ∞) ≠ ω
        intro h; exact absurd h WithTop.coe_ne_top
      · -- ContDiff m (fderiv (I n))
        have h_fderiv_eq : fderiv ℝ (I n) =
            fun y => (curryLeftLIE (E := H) (F := ℝ) n).symm.toContinuousLinearEquiv.toContinuousLinearMap
              (I (n + 1) y) :=
          funext fun y => (I_diff n y).fderiv
        rw [h_fderiv_eq]
        exact ((curryLeftLIE n).symm.toContinuousLinearEquiv.toContinuousLinearMap.contDiff).comp
          (ih (n + 1))
  -- Step 3: iteratedFDeriv m Φ = I m — by induction on m
  have h_iter : ∀ (m : ℕ) (y : H), iteratedFDeriv ℝ m Φ y = I m y := by
    intro m
    induction m with
    | zero =>
      intro y
      -- iteratedFDeriv 0 Φ y = (curry0).symm (Φ y)
      -- I 0 y = ∫ t, (curry0).symm (Φ_t t y)
      -- These are equal because (curry0).symm commutes with the integral
      simp only [iteratedFDeriv_zero_eq_comp, Function.comp_def, hI_def]
      change (continuousMultilinearCurryFin0 ℝ H ℝ).symm (Φ y) =
        ∫ t, (continuousMultilinearCurryFin0 ℝ H ℝ).symm (Φ_t t y)
      set L0 := (continuousMultilinearCurryFin0 ℝ H ℝ).symm.toContinuousLinearEquiv
      exact (L0.toContinuousLinearMap.integral_comp_comm (Φ_t_raw_integrable y)).symm
    | succ m ih =>
      intro y
      -- iteratedFDeriv (m+1) Φ y = (curryLeftLIE m) (fderiv (iteratedFDeriv m Φ) y)
      -- By IH: iteratedFDeriv m Φ = I m, so fderiv (iteratedFDeriv m Φ) = fderiv (I m)
      -- By I_diff: fderiv (I m) y = (curryLeftLIE m).symm (I (m+1) y)
      -- So curryLeftLIE m applied to that gives I (m+1) y
      have h_succ : iteratedFDeriv ℝ (m + 1) Φ y =
          (curryLeftLIE (E := H) (F := ℝ) m) (fderiv ℝ (iteratedFDeriv ℝ m Φ) y) := by
        have := congrFun (iteratedFDeriv_succ_eq_comp_left (𝕜 := ℝ) (f := Φ) (n := m)) y
        rw [Function.comp_def] at this
        rw [this, ← curryLeftLIE_coe_eq]
      rw [h_succ]
      -- Replace iteratedFDeriv m Φ by I m using IH
      have h_eq_fun : iteratedFDeriv ℝ m Φ = I m := funext ih
      rw [h_eq_fun]
      -- Use fderiv from I_diff
      rw [(I_diff m y).fderiv]
      -- curryLeftLIE m applied to (curryLeftLIE m).symm gives identity
      exact (curryLeftLIE m).apply_symm_apply (I (m + 1) y)
  -- Assembly
  refine ⟨?_, fun m y => h_iter m y⟩
  -- ContDiff ∞ Φ: derive from I_contDiff via the I 0 representation
  -- Φ = curry0 ∘ I 0 since I 0 y = curry0.symm (Φ y) by h_iter 0
  have h_Φ_eq : Φ = (continuousMultilinearCurryFin0 ℝ H ℝ) ∘ I 0 := by
    ext y
    simp only [Function.comp_def]
    have h := h_iter 0 y
    simp only [iteratedFDeriv_zero_eq_comp, Function.comp_def] at h
    -- h : curry0.symm (Φ y) = I 0 y
    -- Goal: Φ y = curry0 (I 0 y)
    rw [← h]
    exact ((continuousMultilinearCurryFin0 ℝ H ℝ).apply_symm_apply (Φ y)).symm
  rw [h_Φ_eq]
  exact contDiff_infty.mpr fun m =>
    ((continuousMultilinearCurryFin0 ℝ H ℝ).contDiff).comp (I_contDiff m 0)
