import Mathlib.Data.Nat.Basic
import Mathlib.Tactic

/-! Prime Interval Shell Theory (PIST) Core - Extended Defensible Version

  This module formalizes a defensible discrete core for the PIST state machine.
  It avoids speculative geometry and focuses on an interval-local coordinate model.

  The main idea is that a natural number between consecutive squares is represented
  by a shell index `k` and an offset `t` with `0 ≤ t ≤ 2*k+1`.
  Within that shell, the PIST mass is the quadratic quantity

  `mass = t * ((2*k+1) - t)`.

  This file contains (minimal + extended):

  * Interval-local coordinate type `Coord` with shell geometry
  * Mass / hyperbola-index definitions (a, b, mass = a*b)
  * Mirror involution inside one shell (preserves mass)
  * Zero mass theorems (exactly at shell endpoints)
  * Positive mass equivalence (strictly inside shell)
  * Resonance equivalence relation (refl, symm, trans)
  * Phase flags (grounded/seismic) based on mass
  * Move labels for state-machine transitions
  * LogEntry/Log for append-only history tracking
  * Extended State with operations (penalize, accept, relocate, resonanceJump, rejectWithPenalty)
  * Transition structure with mass preservation and strict decrease
  * LawfulMove inductive (linear, resonance, rejected, crystallized)
  * Projector (idempotent normalizer) and Grounder structures
  * Two kernel interfaces: minimal Kernel and extended KernelExtended
  * Lyapunov-style strict descent guarantees for both kernels

  The file deliberately avoids making cryptographic or physical claims.
  Anything "more weird" is encoded as typed data and admissibility rules.

  Per AGENTS.md §2: PascalCase types, camelCase functions.
  Per AGENTS.md §4: All definitions must have eval witnesses or theorems.
-/

namespace PIST

/-- A coordinate inside the square shell bounded by `k^2` and `(k+1)^2`.
The offset `t` records the position inside that shell, so necessarily
`t ≤ 2*k + 1`.
-/structure Coord where
  k : ℕ
  t : ℕ
  ht : t ≤ 2 * k + 1

  deriving DecidableEq, Repr

namespace Coord

/-- The underlying natural number represented by the shell coordinate. -/
def n (c : Coord) : ℕ := c.k ^ 2 + c.t

/-- Distance to the lower square in shell coordinates. -/
def a (c : Coord) : ℕ := c.t

/-- Distance to the upper square in shell coordinates. -/
def b (c : Coord) : ℕ := 2 * c.k + 1 - c.t

/-- The PIST mass / hyperbola index in shell coordinates. -/
def mass (c : Coord) : ℕ := c.a * c.b

@[simp] theorem a_def (c : Coord) : c.a = c.t := rfl

@[simp] theorem b_def (c : Coord) : c.b = 2 * c.k + 1 - c.t := rfl

@[simp] theorem mass_def (c : Coord) : c.mass = c.t * (2 * c.k + 1 - c.t) := rfl

/-- The shell identity `a + b = 2*k+1`. -/
theorem a_add_b (c : Coord) : c.a + c.b = 2 * c.k + 1 := by
  dsimp [a, b]
  exact Nat.add_sub_of_le c.ht

/-- The mirror point inside the same shell. -/
def mirror (c : Coord) : Coord where
  k := c.k
  t := 2 * c.k + 1 - c.t
  ht := Nat.sub_le _ _

@[simp] theorem mirror_k (c : Coord) : c.mirror.k = c.k := rfl

@[simp] theorem mirror_t (c : Coord) : c.mirror.t = 2 * c.k + 1 - c.t := rfl

@[simp] theorem a_mirror (c : Coord) : c.mirror.a = c.b := rfl

/-- Mirroring swaps the two shell distances. -/
@[simp] theorem b_mirror (c : Coord) : c.mirror.b = c.a := by
  dsimp [b, a, mirror]
  rw [Nat.sub_sub_self c.ht]

/-- Mirror preserves mass. -/
@[simp] theorem mass_mirror (c : Coord) : c.mirror.mass = c.mass := by
  simp [mass, a, b, mirror, Nat.mul_comm]
  have h : c.k * 2 + 1 - (c.k * 2 + 1 - c.t) = c.t := by
    have : c.k * 2 + 1 = 2 * c.k + 1 := by simp [Nat.mul_comm]
    rw [this]
    rw [Nat.sub_sub_self c.ht]
  simp [h]
  exact Nat.mul_comm _ _

/-- Mirroring twice returns the original shell offset. -/
@[simp] theorem mirror_mirror_t (c : Coord) : c.mirror.mirror.t = c.t := by
  dsimp [mirror]
  rw [Nat.sub_sub_self c.ht]

/-- Mirror is an involution. -/
@[simp] theorem mirror_mirror (c : Coord) : c.mirror.mirror = c := by
  cases c with
  | mk k t ht =>
      simp [mirror]
      rw [Nat.sub_sub_self ht]

/-- A coordinate has zero mass exactly at the shell endpoints. -/
theorem mass_eq_zero_iff (c : Coord) : c.mass = 0 ↔ c.t = 0 ∨ c.t = 2 * c.k + 1 := by
  rw [mass_def]
  constructor
  · intro h
    rcases (Nat.mul_eq_zero.mp h) with h0 | h1
    · exact Or.inl h0
    · right
      have hle : 2 * c.k + 1 ≤ c.t := by
        rw [Nat.sub_eq_zero_iff_le] at h1
        exact h1
      exact le_antisymm c.ht hle
  · rintro (h | h)
    · simp [h]
    · simp [h]

/-- Positive mass is equivalent to being strictly inside the shell. -/
theorem mass_pos_iff (c : Coord) : 0 < c.mass ↔ 0 < c.t ∧ c.t < 2 * c.k + 1 := by
  constructor
  · intro h
    have hne : c.mass ≠ 0 := Nat.ne_of_gt h
    have hnot := mt (mass_eq_zero_iff c).mpr hne
    constructor
    · by_contra h0
      apply hne
      apply (mass_eq_zero_iff c).mpr
      exact Or.inl (Nat.eq_zero_of_not_pos h0)
    · by_contra htop
      apply hne
      apply (mass_eq_zero_iff c).mpr
      exact Or.inr (le_antisymm c.ht (not_lt.mp htop))
  · rintro ⟨ht0, httop⟩
    rw [mass_def]
    apply Nat.mul_pos
    · exact ht0
    · exact Nat.sub_pos_of_lt httop

/-- Left shell endpoint. -/
def lower (k : ℕ) : Coord where
  k := k
  t := 0
  ht := by omega

/-- Right shell endpoint. -/
def upper (k : ℕ) : Coord where
  k := k
  t := 2 * k + 1
  ht := by omega

@[simp] theorem mass_lower (k : ℕ) : (lower k).mass = 0 := by simp [lower, mass]
@[simp] theorem mass_upper (k : ℕ) : (upper k).mass = 0 := by simp [upper, mass]

end Coord

/-- Two shell coordinates are resonant when they have equal mass. -/
def Resonant (x y : Coord) : Prop := x.mass = y.mass

theorem Resonant.refl (x : Coord) : Resonant x x := rfl

theorem Resonant.symm {x y : Coord} : Resonant x y -> Resonant y x := by
  intro h
  exact Eq.symm h

theorem Resonant.trans {x y z : Coord} : Resonant x y -> Resonant y z -> Resonant x z := by
  intro h₁ h₂
  exact Eq.trans h₁ h₂

/-- Phase flags for the interval-local machine. -/
inductive Phase
  | grounded
  | drift
  | seismic
  deriving DecidableEq, Repr

/-- Auxiliary resonance metadata. -/
def isResonantPair (x y : Coord) : Bool := x != y && x.mass == y.mass

/-- A simple phase classifier based on zero vs positive mass.
This is intentionally minimal and fully justified from the existing theory.
A richer classifier can be built on top of the same core.
-/def phase (c : Coord) : Phase :=
  if c.mass = 0 then Phase.grounded else Phase.seismic

@[simp] theorem phase_grounded_iff (c : Coord) : phase c = Phase.grounded ↔ c.mass = 0 := by
  apply Iff.intro
  · intro h
    unfold phase at h
    by_cases h2 : c.mass = 0
    · exact h2
    · rw [if_neg h2] at h
      cases h
  · intro h
    unfold phase
    by_cases h2 : c.mass = 0
    · rw [if_pos h2]
    · cases h2 h

@[simp] theorem phase_seismic_iff (c : Coord) : phase c = Phase.seismic ↔ c.mass ≠ 0 := by
  apply Iff.intro
  · intro h
    unfold phase at h
    by_cases h2 : c.mass = 0
    · rw [if_pos h2] at h
      cases h
    · exact h2
  · intro h
    unfold phase
    by_cases h2 : c.mass = 0
    · rw [if_pos h2]
      cases h h2
    · rw [if_neg h2]

/-- Move labels for state-machine transitions. -/
inductive MoveFlag
  | linearStep
  | resonanceJump
  | rejected
  | crystallized
  deriving DecidableEq, Repr

/-- A single log entry for append-only state history. -/
structure LogEntry where
  before : Coord
  after : Coord
  move : MoveFlag
  preservedMass : Bool

  deriving DecidableEq, Repr

/-- Append-only logs. We do not claim cryptographic properties here; this is just
an auditable history shape that a stronger implementation can refine.
-/abbrev Log := List LogEntry

namespace LogEntry

/-- The canonical entry for a resonance jump. -/
def resonance (x y : Coord) : LogEntry :=
  { before := x, after := y, move := MoveFlag.resonanceJump,
    preservedMass := decide (x.mass = y.mass) }

/-- The canonical entry for a rejection event. -/
def rejection (x y : Coord) : LogEntry :=
  { before := x, after := y, move := MoveFlag.rejected,
    preservedMass := decide (x.mass = y.mass) }

end LogEntry

/-- A minimal machine state over interval-local coordinates. -/
structure State where
  pos : Coord
  phaseFlag : Phase
  accepted : List Coord
  rejected : List Coord
  friction : ℕ
  log : Log

  deriving Repr

namespace State

/-- The canonical state built from a position. -/
def ofCoord (c : Coord) : State :=
  { pos := c
    phaseFlag := phase c
    accepted := []
    rejected := []
    friction := 0
    log := [] }

/-- A basic Lyapunov functional: PIST mass plus friction. -/
def potential (S : State) : ℕ := S.pos.mass + S.friction

@[simp] theorem potential_ofCoord (c : Coord) : (ofCoord c).potential = c.mass := by
  simp [ofCoord, potential]

/-- Append a log entry. -/
def appendLog (S : State) (e : LogEntry) : State :=
  { S with log := e :: S.log }

@[simp] theorem appendLog_log (S : State) (e : LogEntry) : (appendLog S e).log = e :: S.log := rfl

/-- Register a rejection and increase friction by a nonnegative penalty. -/
def penalize (S : State) (bad : Coord) (penalty : ℕ) : State :=
  { S with rejected := bad :: S.rejected, friction := S.friction + penalty }

@[simp] theorem penalize_friction (S : State) (bad : Coord) (penalty : ℕ) :
    (penalize S bad penalty).friction = S.friction + penalty := rfl

@[simp] theorem potential_penalize (S : State) (bad : Coord) (penalty : ℕ) :
    (penalize S bad penalty).potential = S.potential + penalty := by
  simp [potential, penalize, Nat.add_assoc]

/-- Register an accepted coordinate. -/
def accept (S : State) (good : Coord) : State :=
  { S with accepted := good :: S.accepted }

/-- Replace the active coordinate and refresh the phase flag. -/
def relocate (S : State) (c : Coord) : State :=
  { S with pos := c, phaseFlag := phase c }

@[simp] theorem relocate_pos (S : State) (c : Coord) : (relocate S c).pos = c := rfl
@[simp] theorem relocate_phase (S : State) (c : Coord) : (relocate S c).phaseFlag = phase c := rfl

/-- A resonance jump preserves the shell mass and updates the active coordinate. -/
def resonanceJump (S : State) (target : Coord) (_h : Resonant S.pos target) : State :=
  appendLog (relocate (accept S target) target) (LogEntry.resonance S.pos target)

@[simp] theorem resonanceJump_pos (S : State) (target : Coord) (h : Resonant S.pos target) :
    (resonanceJump S target h).pos = target := by
  simp [resonanceJump, appendLog, relocate]

@[simp] theorem resonanceJump_potential (S : State) (target : Coord) (h : Resonant S.pos target) :
    (resonanceJump S target h).potential = S.pos.mass + S.friction := by
  simp [resonanceJump, State.potential, relocate, accept, appendLog]
  exact h.symm

/-- A rejection event appends to the log and increases friction. -/
def rejectWithPenalty (S : State) (bad : Coord) (penalty : ℕ) : State :=
  appendLog (penalize S bad penalty) (LogEntry.rejection S.pos bad)

@[simp] theorem rejectWithPenalty_friction (S : State) (bad : Coord) (penalty : ℕ) :
    (rejectWithPenalty S bad penalty).friction = S.friction + penalty := by
  simp [rejectWithPenalty, penalize, appendLog]

@[simp] theorem penalize_pos (S : State) (bad : Coord) (penalty : ℕ) :
    (penalize S bad penalty).pos = S.pos := rfl

@[simp] theorem potential_rejectWithPenalty (S : State) (bad : Coord) (penalty : ℕ) :
    (rejectWithPenalty S bad penalty).potential = S.potential + penalty := by
  simp [rejectWithPenalty, State.potential, appendLog, penalize_pos]
  rw [Nat.add_assoc]

end State

/-- A lawful state-machine kernel. This is a specification interface:
concrete instances must provide the operations and proofs below.
-/structure Kernel (Candidate Reality : Type) where
  bind : Candidate
  assimilate : State → Candidate → State
  project : State → State
  ground : State → Reality → State
  terminal : State → Prop
  step : State → Reality → State := fun S R => ground (project (assimilate S bind)) R
  /-- Projection is idempotent. -/
  project_idem : ∀ S, project (project S) = project S
  /-- Grounding preserves the image of projection in one step form. -/
  project_step : ∀ S R, step S R = ground (project (assimilate S bind)) R
  /-- Nonterminal steps strictly decrease the chosen potential. -/
  strict_descent : ∀ S R, ¬ terminal S → State.potential (step S R) < State.potential S

namespace Kernel

variable {Candidate Reality : Type} (K : Kernel Candidate Reality)

@[simp] theorem step_def (S : State) (R : Reality) :
    K.step S R = K.ground (K.project (K.assimilate S K.bind)) R := by
  exact K.project_step S R

/-- A nonterminal state cannot be a fixed point of a strictly descending step. -/
theorem not_fixed_of_nonterminal (S : State) (R : Reality) (hS : ¬ K.terminal S) :
    K.step S R ≠ S := by
  intro hfix
  have hlt := K.strict_descent S R hS
  rw [hfix] at hlt
  exact Nat.lt_irrefl _ hlt

/-- One-step evolution from a nonterminal state strictly lowers the potential. -/
theorem potential_decreases (S : State) (R : Reality) (hS : ¬ K.terminal S) :
    State.potential (K.step S R) < State.potential S :=
  K.strict_descent S R hS

end Kernel

-- ════════════════════════════════════════════════════════════
-- Extended State Machine (Advanced Interface)
-- ════════════════════════════════════════════════════════════

/-- A transition packages a next state together with the move label used to reach it. -/
structure Transition where
  next : State
  flag : MoveFlag

  deriving Repr

namespace Transition

/-- Whether the transition preserves shell mass at the active coordinate. -/
def PreservesMass (S : State) (T : Transition) : Prop := S.pos.mass = T.next.pos.mass

/-- Whether the transition strictly decreases the potential. -/
def StrictlyDecreases (S : State) (T : Transition) : Prop := T.next.potential < S.potential

end Transition

/-- Lawfulness for candidate operations: either a one-step linear move, a resonance jump,
or a rejection that stays in place while adding friction.
-/inductive LawfulMove (S : State) : Transition → Prop
  | linear (T : Transition)
      (hflag : T.flag = MoveFlag.linearStep)
      (hfric : T.next.friction = S.friction)
      (hstep : T.next.pos.k = S.pos.k)
      (hshift : T.next.pos.t + 1 = S.pos.t ∨ S.pos.t + 1 = T.next.pos.t) :
      LawfulMove S T
  | resonance (target : Coord) (hres : Resonant S.pos target) :
      LawfulMove S
        { next := S.resonanceJump target hres, flag := MoveFlag.resonanceJump }
  | rejected (bad : Coord) (penalty : ℕ) :
      LawfulMove S
        { next := S.rejectWithPenalty bad penalty, flag := MoveFlag.rejected }
  | crystallized (target : Coord)
      (hzero : target.mass = 0)
      (hfric : S.friction = 0) :
      LawfulMove S
        { next := State.ofCoord target, flag := MoveFlag.crystallized }

namespace LawfulMove

/-- Resonance jumps preserve shell mass at the active coordinate. -/
theorem preservesMass_resonance (S : State) (target : Coord) (hres : Resonant S.pos target) :
    Transition.PreservesMass S
      { next := S.resonanceJump target hres, flag := MoveFlag.resonanceJump } := by
  dsimp [Transition.PreservesMass]
  simp [State.resonanceJump_pos]
  exact hres

/-- Rejection with positive penalty strictly increases potential, hence cannot be used as
a descent step.
-/theorem reject_not_descent (S : State) (bad : Coord) {penalty : ℕ} (_hpen : 0 < penalty) :
    ¬ Transition.StrictlyDecreases S
      { next := S.rejectWithPenalty bad penalty, flag := MoveFlag.rejected } := by
  intro hdec
  dsimp [Transition.StrictlyDecreases] at hdec
  rw [State.potential_rejectWithPenalty] at hdec
  exact Nat.not_lt.mpr (Nat.le_add_right _ _) hdec

end LawfulMove

/-- A lawful projection is an idempotent normalizer on states. -/
structure Projector where
  project : State → State
  idem : ∀ S, project (project S) = project S

namespace Projector

@[simp] theorem idem_apply (P : Projector) (S : State) : P.project (P.project S) = P.project S :=
  P.idem S

end Projector

/-- A grounding operator chooses a next state from a lawful candidate and an external
reality parameter.
-/structure Grounder (Reality : Type) where
  ground : State → Reality → State

/-- A more structured kernel than the minimal core: it explicitly tracks a projector,
a grounding map, and a chosen lawful transition policy.
-/structure KernelExtended (Reality : Type) where
  projector : Projector
  grounder : Grounder Reality
  terminal : State → Prop
  choose : State → Reality → Transition
  lawful_choose : ∀ S R, LawfulMove (projector.project S) (choose (projector.project S) R)
  strict_descent : ∀ S R,
    ¬ terminal (projector.project S) →
    Transition.StrictlyDecreases (projector.project S) (choose (projector.project S) R)
  grounded_step : State → Reality → State := fun S R =>
    (grounder.ground (choose (projector.project S) R).next R)

namespace KernelExtended

variable {Reality : Type} (K : KernelExtended Reality)

/-- On projected nonterminal states, the chosen transition strictly decreases the potential. -/
theorem chosen_transition_decreases (S : State) (R : Reality)
    (hS : ¬ K.terminal (K.projector.project S)) :
    Transition.StrictlyDecreases (K.projector.project S) (K.choose (K.projector.project S) R) :=
  K.strict_descent S R hS

/-- A projected nonterminal state cannot be a fixed point of the chosen transition. -/
theorem chosen_transition_not_fixed (S : State) (R : Reality)
    (hS : ¬ K.terminal (K.projector.project S)) :
    (K.choose (K.projector.project S) R).next ≠ K.projector.project S := by
  intro hfix
  have hlt := K.chosen_transition_decreases S R hS
  dsimp [Transition.StrictlyDecreases] at hlt
  rw [hfix] at hlt
  exact Nat.lt_irrefl _ hlt

end KernelExtended

-- ════════════════════════════════════════════════════════════
-- Verification Examples (AGENTS.md §4 requirement)
-- ════════════════════════════════════════════════════════════

#eval ({ k := 5, t := 3, ht := by omega : Coord }).mass
#eval ({ k := 5, t := 0, ht := by omega : Coord }).mass
#eval ({ k := 5, t := 3, ht := by omega : Coord }).mirror.mass = ({ k := 5, t := 3, ht := by omega : Coord }).mass
#eval ({ k := 5, t := 0, ht := by omega : Coord }).mirror.t
#eval isResonantPair { k := 3, t := 2, ht := by omega } { k := 3, t := 5, ht := by omega }
#eval phase { k := 10, t := 5, ht := by omega : Coord }

end PIST
