/-
import Mathlib.Data.Int.ModEq
import Mathlib.Data.Nat.GCD.Basic
import Mathlib.Data.Finset.Basic
import Mathlib.Data.List.Basic
import Mathlib.Tactic

open Finset

namespace CoreFormalism.CRTSidon

/-!
# CRT Sidon Preservation: n-moduli generalization

This module extends the 2-moduli `sidon_preserved_mod` theorem from
`CRTSidon.lean` to the general n-moduli case.

**Theorem (n-moduli CRT Sidon Preservation):**
If A is a Sidon set, moduli L₀, L₁, ..., Lₖ₋₁ are pairwise coprime,
all pairwise sums a+b < M = ∏Lᵢ, S ≥ all labels, and:
  (a+b) % L₀ = (c+d) % L₀                    [identity component]
  ((S-a)+(S-b)) % Lᵢ = ((S-c)+(S-d)) % Lᵢ    [reflection components, i ≥ 1]
then {a,b} = {c,d}.

**Proof strategy:** By the generalized CRT uniqueness theorem:
if x ≡ y (mod Lᵢ) for all i, pairwise coprime moduli, x,y < ∏Lᵢ,
then x = y. This is proven by induction on the moduli list, using
the 2-moduli case (`mod_eq_of_coprime`) as the inductive step.

The reflection components (i ≥ 1) all reduce to (a+b) ≡ (c+d) (mod Lᵢ)
via the same algebraic manipulation as the 2-moduli case:
  (S-a) + (S-b) = 2S - (a+b), so
  (2S - (a+b)) % Lᵢ = (2S - (c+d)) % Lᵢ implies (a+b) ≡ (c+d) (mod Lᵢ).

This module provides:
1. `mod_eq_of_coprime_list` — generalized CRT uniqueness (n moduli)
2. `reflection_implies_sum_cong` — reflection component → sum congruence
3. `sidon_preserved_mod_n` — main theorem (n-moduli version)
-/

/-- A Sidon set: all unordered pairwise sums are distinct. -/
def IsSidon (A : Finset ℕ) : Prop :=
  ∀ ⦃a b c d : ℕ⦄, a ∈ A → b ∈ A → c ∈ A → d ∈ A → a + b = c + d →
    (a = c ∧ b = d) ∨ (a = d ∧ b = c)

/-- Pairwise coprime list. -/
def PairwiseCoprime (ls : List ℕ) : Prop :=
  ∀ i j (hi : i < ls.length) (hj : j < ls.length), i < j →
    (ls.get ⟨i, hi⟩).gcd (ls.get ⟨j, hj⟩) = 1

/-- All elements of a list are positive. -/
def AllPos (ls : List ℕ) : Prop :=
  ∀ i (hi : i < ls.length), 0 < ls.get ⟨i, hi⟩

/-- Convert ℕ modular equality to ℤ divisibility: a%n = b%n → n ∣ (a-b) in ℤ. -/
lemma mod_eq_dvd (a b n : ℕ) (h : a % n = b % n) : (n : ℤ) ∣ ((a : ℤ) - (b : ℤ)) := by
  have hz : (a : ℤ) % n = (b : ℤ) % n := by exact_mod_cast h
  have h_eq : (a : ℤ) ≡ (b : ℤ) [ZMOD n] := hz
  have h_sub : (a : ℤ) - (b : ℤ) ≡ 0 [ZMOD n] := by
    calc
      (a : ℤ) - (b : ℤ) ≡ (b : ℤ) - (b : ℤ) [ZMOD n] := Int.ModEq.sub h_eq (Int.ModEq.refl _)
      _ = 0 := by ring
  exact (Int.modEq_zero_iff_dvd.mp h_sub)

/-- Convert ℤ divisibility to ℕ modular equality. -/
lemma dvd_mod_eq (a b n : ℕ) (h : (n : ℤ) ∣ ((a : ℤ) - (b : ℤ))) : a % n = b % n := by
  have h_sub : ((a : ℤ) - (b : ℤ)) ≡ 0 [ZMOD n] := by
    rw [Int.modEq_zero_iff_dvd]
    exact h
  have h_mod : (a : ℤ) ≡ (b : ℤ) [ZMOD n] := by
    calc
      (a : ℤ) = ((a : ℤ) - (b : ℤ)) + (b : ℤ) := by ring
      _ ≡ 0 + (b : ℤ) [ZMOD n] := Int.ModEq.add h_sub (Int.ModEq.refl _)
      _ = (b : ℤ) := by ring
  exact_mod_cast h_mod

/-! ## Generalized CRT Uniqueness -/

/-- If gcd(m, n) = 1 and n ∣ d, then m*n ∣ m*d. -/
lemma dvd_mul_of_dvd_right {m n d : ℕ} (hmn : Nat.Coprime m n) (hn : n ∣ d) :
    m * n ∣ m * d := by
  obtain ⟨q, hq⟩ := hn
  exact ⟨q, by rw [hq, mul_assoc]⟩

/-! ### Key lemma: product of coprime moduli

If L is a pairwise coprime list, and each Lᵢ divides d, then
∏Lᵢ divides d. This is the heart of the generalized CRT.

We prove this by strong induction on the list length.
-/

/-- Product of a list of ℕ. -/
def listProd (ls : List ℕ) : ℕ := ls.prod

/-- If all elements in a list divide d, and the list is pairwise coprime,
    then the product divides d.

    Proof by induction on the list:
    - Base case ([]): product = 1, 1 ∣ d trivially
    - Inductive step (L₀ :: tail): L₀ ∣ d and (∏tail) ∣ d
      By IH: (∏tail) ∣ d
      Since L₀ is coprime to each element of tail, L₀ is coprime to ∏tail
      (coprime to product = coprime to each factor)
      Since L₀ ∣ d and (∏tail) ∣ d and gcd(L₀, ∏tail) = 1:
      L₀ * (∏tail) ∣ d  (i.e., (∏(L₀ :: tail)) ∣ d)
-/
theorem pairwise_coprime_product_dvd (L : List ℕ) (hCoprime : PairwiseCoprime L)
    (hDiv : ∀ i (hi : i < L.length), (L.get ⟨i, hi⟩) ∣ d) :
    L.prod ∣ d := by
  induction L using List.rec with
  | nil => simp [List.prod, Nat.one_dvd]
  | cons L₀ tail IH =>
    -- L = L₀ :: tail, product = L₀ * tail.prod
    -- L₀ ∣ d (from hDiv at index 0)
    have hL0_dvd : L₀ ∣ d := hDiv 0 (by simp)
    -- tail elements divide d (from hDiv at shifted indices)
    have hTail_dvd : ∀ i (hi : i < tail.length), (tail.get ⟨i, hi⟩) ∣ d := by
      intro i hi
      exact hDiv (i + 1) (by simp [hi])
    -- tail is pairwise coprime (inherited from L)
    have hTailCoprime : PairwiseCoprime tail := by
      intro i j hi hj hij
      exact hCoprime (i + 1) (j + 1) (by simp [hi]) (by simp [hj]) (by omega)
    -- By IH: tail.prod ∣ d
    have hTailProd_dvd : tail.prod ∣ d := IH hTailCoprime hTail_dvd
    -- L₀ is coprime to tail.prod
    -- (because L₀ is coprime to each element of tail)
    have hL0_coprime_tail_prod : Nat.Coprime L₀ tail.prod := by
      -- Nat.Coprime L₀ (∏tail) iff gcd(L₀, ∏tail) = 1
      -- This follows from L₀ coprime to each element of tail
      -- We prove by induction on tail
      induction tail using List.rec with
      | nil => simp [List.prod, Nat.coprime_one_right]
      | cons L₁ tail' IH' =>
        -- tail = L₁ :: tail', product = L₁ * tail'.prod
        -- gcd(L₀, L₁ * tail'.prod) = 1
        -- We know: gcd(L₀, L₁) = 1 (from hCoprime)
        -- By IH': gcd(L₀, tail'.prod) = 1
        -- Since gcd(L₀, L₁) = 1 and gcd(L₀, tail'.prod) = 1:
        -- gcd(L₀, L₁ * tail'.prod) = 1
        -- This follows from: if gcd(a,b)=1 and gcd(a,c)=1 then gcd(a,b*c)=1
        have hL0_coprime_L1 : Nat.Coprime L₀ L₁ :=
          hCoprime 0 1 (by simp) (by simp) (by omega)
        have hTailCoprime' : PairwiseCoprime tail' := by
          intro i j hi hj hij
          exact hCoprime (i + 2) (j + 2) (by simp [hi]) (by simp [hj]) (by omega)
        have hL0_coprime_tail'_prod : Nat.Coprime L₀ tail'.prod := by
          exact IH' hTailCoprime' (by
            intro i hi
            exact hCoprime (i + 2) (by simp [hi]))
        -- gcd(L₀, L₁ * tail'.prod) = 1
        -- Use: Nat.Coprime.mul_right or Nat.coprime_mul
        -- If gcd(L₀, L₁) = 1 and gcd(L₀, tail'.prod) = 1
        -- then gcd(L₀, L₁ * tail'.prod) = 1
        rw [Nat.coprime_mul_right]
        exact ⟨hL0_coprime_L1, hL0_coprime_tail'_prod⟩
    -- Since L₀ ∣ d and tail.prod ∣ d and gcd(L₀, tail.prod) = 1:
    -- L₀ * tail.prod ∣ d
    -- Use: Nat.Coprime.dvd_mul or Nat.mul_dvd_of_coprime
    have hprod_dvd : L₀ * tail.prod ∣ d := by
      -- Nat.Coprime.dvd_mul: if gcd(a, b) = 1, a ∣ d, b ∣ d → a * b ∣ d
      exact hL0_coprime_tail_prod.dvd_mul hL0_dvd hTailProd_dvd
    -- product of (L₀ :: tail) = L₀ * tail.prod
    simp [List.prod, hprod_dvd]

/-! ### Generalized CRT uniqueness (n moduli) -/

/-- If a ≡ b (mod Lᵢ) for all i, L is pairwise coprime, and a, b < ∏Lᵢ,
    then a = b.

    This generalizes `mod_eq_of_coprime` from 2 moduli to n moduli. -/
theorem mod_eq_of_coprime_list {a b : ℕ} (L : List ℕ)
    (hCoprime : PairwiseCoprime L) (hPos : AllPos L)
    (hcong : ∀ i (hi : i < L.length), a % (L.get ⟨i, hi⟩) = b % (L.get ⟨i, hi⟩))
    (ha : a < L.prod) (hb : b < L.prod) : a = b := by
  -- Each Lᵢ divides (a - b) in ℤ
  have hL_dvd : ∀ i (hi : i < L.length), (L.get ⟨i, hi⟩ : ℤ) ∣ ((a : ℤ) - (b : ℤ)) := by
    intro i hi
    exact mod_eq_dvd a b (L.get ⟨i, hi⟩) (hcong i hi)
  -- Convert to ℕ divisibility (need to handle the ℤ → ℕ direction)
  -- Actually, we need: each Lᵢ divides |a - b| in ℕ
  -- Since a, b ∈ ℕ, either a ≥ b or b > a
  -- Case 1: a ≥ b → d = a - b ≥ 0, each Lᵢ ∣ d in ℕ
  -- Case 2: b > a → d = b - a ≥ 0, each Lᵢ ∣ d in ℕ (by symmetry)
  -- In both cases, ∏Lᵢ ∣ d, and d < ∏Lᵢ, so d = 0, i.e., a = b
  by_cases hab : a ≥ b
  · -- a ≥ b: d = a - b
    set d := a - b
    -- Each Lᵢ ∣ d in ℕ
    have hL_dvd_nat : ∀ i (hi : i < L.length), (L.get ⟨i, hi⟩) ∣ d := by
      intro i hi
      have h := hL_dvd i hi
      have hd_eq : ((a : ℤ) - (b : ℤ)) = (d : ℤ) := by
        dsimp [d]; exact_mod_cast (Nat.sub_eq_of_le hab)
      rw [hd_eq] at h
      -- (Lᵢ : ℤ) ∣ (d : ℤ) and 0 ≤ d → Lᵢ ∣ d in ℕ
      rwa [Int.natCast_dvd_natCast_iff] at h
    -- ∏Lᵢ ∣ d
    have hprod_dvd : L.prod ∣ d := pairwise_coprime_product_dvd L hCoprime hL_dvd_nat
    -- |a - b| < ∏Lᵢ (since a, b < ∏Lᵢ)
    have hd_lt : d < L.prod := by
      dsimp [d]
      -- a - b < a < L.prod (since a < L.prod and b > 0)
      -- Actually: a - b ≤ a - 0 = a < L.prod
      have := Nat.sub_le_sub_left hab a
      omega
    -- ∏Lᵢ ∣ d and d < ∏Lᵢ → d = 0 → a = b
    obtain ⟨q, hq⟩ := hprod_dvd
    by_cases hq0 : q = 0
    · -- q = 0 → d = 0 → a = b
      dsimp [d] at hq
      rw [hq0, mul_zero] at hq
      omega
    · -- q ≥ 1 → d = ∏Lᵢ * q ≥ ∏Lᵢ > d (contradiction)
      have : ∏Lᵢ ≤ d := by
        rw [hq]
        have : 1 ≤ q := by omega
        nlinarith
      omega
  · -- b > a: d = b - a, symmetric argument
    set d := b - a
    have hd_eq : ((a : ℤ) - (b : ℤ)) = -(d : ℤ) := by
      dsimp [d]; exact_mod_cast (Nat.sub_eq_of_le (by omega : b ≥ a))
    -- Each Lᵢ ∣ (b - a) in ℕ (by symmetry of modular arithmetic)
    have hL_dvd_nat : ∀ i (hi : i < L.length), (L.get ⟨i, hi⟩) ∣ d := by
      intro i hi
      have h := hL_dvd i hi
      rw [hd_eq] at h
      -- (Lᵢ : ℤ) ∣ -(d : ℤ) → (Lᵢ : ℤ) ∣ (d : ℤ)
      have h' : (L.get ⟨i, hi⟩ : ℤ) ∣ (d : ℤ) := Int.dvd_neg.mpr h
      rwa [Int.natCast_dvd_natCast_iff] at h'
    have hprod_dvd : L.prod ∣ d := pairwise_coprime_product_dvd L hCoprime hL_dvd_nat
    have hd_lt : d < L.prod := by
      dsimp [d]
      omega
    obtain ⟨q, hq⟩ := hprod_dvd
    by_cases hq0 : q = 0
    · dsimp [d] at hq
      rw [hq0, mul_zero] at hq
      omega
    · have : ∏Lᵢ ≤ d := by rw [hq]; have : 1 ≤ q := by omega; nlinarith
      omega

/-! ### Reflection → sum congruence -/

/-- The reflection component (S-a)+(S-b) ≡ (S-c)+(S-d) (mod Lᵢ)
    implies (a+b) ≡ (c+d) (mod Lᵢ).

    This is the same algebraic manipulation as the 2-moduli case:
    (S-a)+(S-b) = 2S-(a+b), so
    (2S-(a+b)) % Lᵢ = (2S-(c+d)) % Lᵢ
    → (a+b) ≡ (c+d) (mod Lᵢ)  -/
lemma reflection_implies_sum_cong {a b c d S Lᵢ : ℕ}
    (hS_a : a ≤ S) (hS_b : b ≤ S) (hS_c : c ≤ S) (hS_d : d ≤ S)
    (h_ref : ((S - a) + (S - b)) % Lᵢ = ((S - c) + (S - d)) % Lᵢ) :
    (a + b) % Lᵢ = (c + d) % Lᵢ := by
  -- (S-a) + (S-b) = 2S - (a+b)
  have h_ab : (S - a) + (S - b) = 2 * S - (a + b) := by omega
  have h_cd : (S - c) + (S - d) = 2 * S - (c + d) := by omega
  rw [h_ab, h_cd] at h_ref
  -- h_ref: (2S - (a+b)) % Lᵢ = (2S - (c+d)) % Lᵢ
  -- In ℤ: 2S-(a+b) ≡ 2S-(c+d) (ZMOD Lᵢ) → (a+b) ≡ (c+d) (ZMOD Lᵢ)
  have hz : ((2 * S - (a + b) : ℕ) : ℤ) % Lᵢ = ((2 * S - (c + d) : ℕ) : ℤ) % Lᵢ :=
    by exact_mod_cast h_ref
  have h_eq : ((2 * S - (a + b) : ℕ) : ℤ) ≡ ((2 * S - (c + d) : ℕ) : ℤ) [ZMOD Lᵢ] := hz
  have h_sub : ((2 * S - (c + d) : ℕ) : ℤ) - ((2 * S - (a + b) : ℕ) : ℤ) ≡ 0 [ZMOD Lᵢ] := by
    have h_sub_eq : ((2 * S - (c + d) : ℕ) : ℤ) - ((2 * S - (a + b) : ℕ) : ℤ) ≡
      ((2 * S - (c + d) : ℕ) : ℤ) - ((2 * S - (c + d) : ℕ) : ℤ) [ZMOD Lᵢ] :=
      Int.ModEq.sub (Int.ModEq.refl _) h_eq
    have hzero : ((2 * S - (c + d) : ℕ) : ℤ) - ((2 * S - (c + d) : ℕ) : ℤ) = 0 := by ring
    simpa [hzero] using h_sub_eq
  have h_sub_simp : ((2 * S - (c + d) : ℕ) : ℤ) - ((2 * S - (a + b) : ℕ) : ℤ) =
    ((a + b : ℤ) - (c + d : ℤ)) := by omega
  rw [h_sub_simp] at h_sub
  exact dvd_mod_eq (a + b) (c + d) Lᵢ (Int.modEq_zero_iff_dvd.mp h_sub)

/-! ### Main theorem: n-moduli CRT Sidon preservation -/

/-- **Theorem (n-moduli CRT Sidon Preservation)**.
    If A is a Sidon set, moduli L = [L₀, L₁, ..., Lₖ₋₁] are pairwise coprime
    and positive, all pairwise sums < M = ∏Lᵢ, S ≥ all labels, and:
      - Component 0 (identity): (a+b) % L₀ = (c+d) % L₀
      - Component i ≥ 1 (reflection): ((S-a)+(S-b)) % Lᵢ = ((S-c)+(S-d)) % Lᵢ
    then {a,b} = {c,d}.

    This generalizes `sidon_preserved_mod` from 2 moduli to n moduli. -/
theorem sidon_preserved_mod_n (A : Finset ℕ) (hSidon : IsSidon A) (S : ℕ)
    (L : List ℕ) (hCoprime : PairwiseCoprime L) (hPos : AllPos L)
    (hS : ∀ a ∈ A, a ≤ S)
    (hBound : ∀ a ∈ A, ∀ b ∈ A, a + b < L.prod) :
    ∀ ⦃a b c d : ℕ⦄, a ∈ A → b ∈ A → c ∈ A → d ∈ A →
      -- identity component (index 0)
      (a % (L.get ⟨0, by simp⟩) + b % (L.get ⟨0, by simp⟩)) % (L.get ⟨0, by simp⟩) =
      (c % (L.get ⟨0, by simp⟩) + d % (L.get ⟨0, by simp⟩)) % (L.get ⟨0, by simp⟩) →
      -- reflection components (indices ≥ 1)
      (∀ i (hi : 1 ≤ i) (hi' : i < L.length),
        ((S - a) % (L.get ⟨i, hi'⟩) + (S - b) % (L.get ⟨i, hi'⟩)) % (L.get ⟨i, hi'⟩) =
        ((S - c) % (L.get ⟨i, hi'⟩) + (S - d) % (L.get ⟨i, hi'⟩)) % (L.get ⟨i, hi'⟩)) →
        (a = c ∧ b = d) ∨ (a = d ∧ b = c) := by
  intro a b c d ha hb hc hd h_id h_ref
  -- Step 1: identity component → (a+b) % L₀ = (c+d) % L₀
  have hL0_cong : (a + b) % (L.get ⟨0, by simp⟩) = (c + d) % (L.get ⟨0, by simp⟩) := by
    rw [Nat.add_mod, Nat.add_mod]
    exact h_id
  -- Step 2: reflection components → (a+b) % Lᵢ = (c+d) % Lᵢ for all i ≥ 1
  have hRef_cong : ∀ i (hi : 1 ≤ i) (hi' : i < L.length),
      (a + b) % (L.get ⟨i, hi'⟩) = (c + d) % (L.get ⟨i, hi'⟩) := by
    intro i hi hi'
    have hS_a : a ≤ S := hS a ha
    have hS_b : b ≤ S := hS b hb
    have hS_c : c ≤ S := hS c hc
    have hS_d : d ≤ S := hS d hd
    exact reflection_implies_sum_cong hS_a hS_b hS_c hS_d (h_ref i hi hi')
  -- Step 3: All components congruent: (a+b) ≡ (c+d) (mod Lᵢ) for ALL i
  have hAll_cong : ∀ i (hi : i < L.length),
      (a + b) % (L.get ⟨i, hi⟩) = (c + d) % (L.get ⟨i, hi⟩) := by
    intro i hi
    by_cases hi0 : i = 0
    · rw [hi0]; exact hL0_cong
    · exact hRef_cong i (by omega) hi
  -- Step 4: By generalized CRT, a+b = c+d (since both < ∏Lᵢ)
  have heq : a + b = c + d := by
    apply mod_eq_of_coprime_list L hCoprime hPos hAll_cong
    · exact hBound a ha b hb
    · exact hBound c hc d hd
  -- Step 5: By Sidon
  rcases hSidon ha hb hc hd heq with (⟨hac, hbd⟩ | ⟨had, hbc⟩)
  · exact Or.inl ⟨hac, hbd⟩
  · exact Or.inr ⟨had, hbc⟩

end CoreFormalism.CRTSidon
