/-
Copyright (c) 2026 Edwin Fernando. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Edwin Fernando
-/

import Bppl.Lilac.HilbertCube
import Mathlib.MeasureTheory.Measure.FiniteMeasureProd
import Mathlib.MeasureTheory.Measure.RegularityCompacts
import Mathlib.Topology.EMetricSpace.Paracompact
import Mathlib.Topology.UniformSpace.Uniformizable

/-!
Helper lemmas for the measure product condition in `wp_meas`. Yet to be refactored
-/

open MeasureTheory MeasurableSpace HC unitInterval

noncomputable section

/-! ### Measurability sub-lemmas -/

lemma pre_measurable_in_combined (n : ℕ)
    (ms_pre : MeasurableSpace (Fin n → I))
    {E : Set HC}
    (hE : MeasurableSet[unSplitBi (ms_pre ×ₘ Inf_nil)] E) :
    MeasurableSet[unSplitTri (ms_pre ×ₘ I_borel ×ₘ Inf_nil)] E :=
  (commute_with_add_dim ms_pre ▸ le_sup_right : unSplitBi _ ≤ _) _ hE

lemma coord_measurable_in_combined (n : ℕ)
    (ms_pre : MeasurableSpace (Fin n → I))
    {F : Set HC}
    (hF : MeasurableSet[N_nil_I_borel n] F) :
    MeasurableSet[unSplitTri (ms_pre ×ₘ I_borel ×ₘ Inf_nil)] F :=
  (commute_with_add_dim ms_pre ▸ le_sup_left : N_nil_I_borel n ≤ _) _ hF

/-! ### Helper lemmas for the product factorization -/

/-- `N_nil_I_borel n` is the comap of `I_borel` through `(· n)`. -/
lemma N_nil_I_borel_eq_comap_coord (n : ℕ) :
    N_nil_I_borel n = (inferInstance : MeasurableSpace I).comap (· n : HC → I) := by
  simp only [N_nil_I_borel, unSplitTri, splitTri, MeasurableSpace.prod, N_nil,
    MeasurableSpace.comap_bot, MeasurableSpace.comap_comp, bot_sup_eq, sup_bot_eq]
  congr! 1

/-
If `unSplitBi (ms_pre ×ₘ Inf_nil) ≤ Inf_borel`, then `ms_pre ≤ pi`.
-/
lemma ms_pre_le_pi_of_le_Inf_borel (n : ℕ) (ms_pre : MeasurableSpace (Fin n → I))
    (h : unSplitBi (ms_pre ×ₘ Inf_nil) ≤ Inf_borel) :
    ms_pre ≤ (MeasurableSpace.pi : MeasurableSpace (Fin n → I)) := by
  contrapose! h
  simp_all +decide only [comap_le_iff_le_map, MeasurableEquiv.map_eq]
  intro H
  refine h ?_
  intro s hs
  convert H _ (MeasurableSet.prod hs MeasurableSet.univ) using 1
  constructor <;> intro h
  · exact MeasurableSet.prod h MeasurableSet.univ
  · convert h.preimage (f := fun x : Fin n → I => (x, fun _ : ℕ => (0 : I)))
      (measurable_id.prodMk measurable_const) using 1
    aesop

/-- `(splitBi n).symm ⁻¹' ((Prod.fst ∘ splitBi n) ⁻¹' A) = Prod.fst ⁻¹' A`. -/
lemma splitBi_symm_preimage_fst (n : ℕ) (A : Set (Fin n → I)) :
    (splitBi n).symm ⁻¹' ((Prod.fst ∘ splitBi n) ⁻¹' A) = Prod.fst ⁻¹' A := by
  rw [Set.preimage_comp]
  exact (splitBi n).symm_preimage_preimage _

/-- `(splitBi n).symm ⁻¹' ((· n) ⁻¹' B) = Prod.snd ⁻¹' ((· 0) ⁻¹' B)`. -/
lemma splitBi_symm_preimage_coord_n (n : ℕ) (B : Set I) :
    (splitBi n).symm ⁻¹' ((· n : HC → I) ⁻¹' B) =
    Prod.snd ⁻¹' ((· 0 : HC → I) ⁻¹' B) := by
  ext ⟨a, b⟩
  simp only [Set.mem_preimage]
  unfold splitBi MeasurableEquiv.sumPiEquivProdPi MeasurableEquiv.piCongrLeft
    MeasurableEquiv.trans Equiv.piCongrLeft
  simp +decide

/-- For the i.i.d. product measure, the k-th marginal equals `volume`. -/
lemma infinitePiNat_coord_marginal (k : ℕ) (B : Set I) (hB : MeasurableSet B) :
    Measure.infinitePiNat (fun _ => lebI) ((· k : HC → I) ⁻¹' B) =
    volume B := by
  have hk : k ∈ ({k} : Finset ℕ) := Finset.mem_singleton.mpr rfl
  have h_preimage :
      ((· k : HC → I) ⁻¹' B) =
        (({k} : Finset ℕ).restrict (π := fun _ => I)) ⁻¹' ((fun x => x ⟨k, hk⟩) ⁻¹' B) := rfl
  have h_pi :
      (fun x : ({k} : Finset ℕ) → I => x ⟨k, hk⟩) ⁻¹' B = Set.univ.pi (fun _ => B) := by
    ext x
    constructor
    · rintro h ⟨val, property⟩ -
      obtain rfl : val = k := Finset.mem_singleton.mp property
      exact h
    · intro h
      exact h ⟨k, hk⟩ (Set.mem_univ _)
  rw [h_preimage,
    ← Measure.map_apply (Finset.measurable_restrict _) (measurable_pi_apply _ hB),
    Measure.infinitePiNat_map_restrict, h_pi, Measure.pi_pi]
  simp

/-! ### The main factorization -/

/--
Core computation for `wp_unif_measure_product`, separated to avoid instance conflicts
with `ms_pre`. Here `A` and `B_I` are measurable in the standard (pi/Borel) instances.
-/
lemma wp_unif_measure_product_core
    (n : ℕ) (μ : @ProbabilityMeasure HC Inf_borel)
    {E F : Set HC} {A : Set (Fin n → I)} {B_I : Set I}
    (hA_pi : MeasurableSet A) (hB_I : MeasurableSet B_I)
    (hE_eq : E = (Prod.fst ∘ splitBi n) ⁻¹' A)
    (hF_eq : F = (· n : HC → I) ⁻¹' B_I) :
    ((μ.map (measurable_fst.comp (HC.splitBi n).measurable).aemeasurable |>.prod
      lebHC).map (HC.splitBi n).symm.measurable.aemeasurable).1 (E ∩ F) =
    μ.1 E * lebHC.toMeasure F := by
  subst hE_eq hF_eq
  change ((μ.toMeasure.map _).prod
      (Measure.infinitePiNat (fun _ => (volume : Measure I)))).map (splitBi n).symm
      ((Prod.fst ∘ ⇑(splitBi n) ⁻¹' A) ∩ ((fun x => x n) ⁻¹' B_I)) = _
  rw [Measure.map_apply (splitBi n).symm.measurable
        (hA_pi.preimage (by fun_prop) |>.inter (hB_I.preimage (by fun_prop))),
    show (splitBi n).symm ⁻¹' (Prod.fst ∘ ⇑(splitBi n) ⁻¹' A ∩ (fun x => x n) ⁻¹' B_I) =
      A ×ˢ ((fun x => x 0) ⁻¹' B_I) from by
      ext ⟨x, y⟩; simp [splitBi_symm_preimage_fst, splitBi_symm_preimage_coord_n],
    Measure.prod_prod,
    Measure.map_apply (by fun_prop) hA_pi,
    infinitePiNat_coord_marginal _ _ hB_I]
  change _ = μ.toMeasure _ * Measure.infinitePiNat (fun _ => (volume : Measure I)) _
  rw [infinitePiNat_coord_marginal _ _ hB_I]

end
