/-
BindingSite.BindingSiteHachimoji — Amino acid encoding and Fisher metric.

Defines the computable core of the binding site pipeline and the noncomputable
Fisher metric used in the §5 theorems of BindingSiteEntropy.lean.

Chentsov uniqueness (ChentsovFinite.lean) is quarantined — it has 15+ sorries
and is a deep geometry result.  This file provides the amino acid / state
machinery WITHOUT depending on the Chentsov proof.
-/
import BindingSite.BindingSiteTypes
import Mathlib.Data.Real.Basic
import Mathlib.Data.Fin.Basic

namespace BindingSite

open SilverSight.FixedPoint

-- ═══════════════════════════════════════════════════════════════════════════
-- §1  Amino acid probability distribution (for math §5 only)
-- ═══════════════════════════════════════════════════════════════════════════

/-- A probability distribution over 50 atom types (Void-X parameterization).
    Used only in noncomputable Fisher metric theorems; not in the compute path. -/
structure AminoAcidDistribution where
  val    : Fin 50 → ℝ
  pos    : ∀ i, 0 < val i
  sum_eq : ∑ i, val i = 1

-- ═══════════════════════════════════════════════════════════════════════════
-- §2  Fisher metric (noncomputable — appears only in math section)
-- ═══════════════════════════════════════════════════════════════════════════

/-- Fisher information metric on Δ⁴⁹ (50-dimensional probability simplex).
    Diagonal form: g_ij(p) = δ_ij / p_i.
    Uniqueness up to positive scalar: Chentsov's theorem (quarantined). -/
noncomputable def fisherMetric50 (p : AminoAcidDistribution) (i j : Fin 50) : ℝ :=
  if i = j then 1 / p.val i else 0

-- ═══════════════════════════════════════════════════════════════════════════
-- §3  Dominant state extractor
-- ═══════════════════════════════════════════════════════════════════════════

/-- Return the most common BindingSiteState in a list of classified residues.
    Tie-breaking: earlier states win. -/
def dominantState (residues : List BindingSiteResidue) : BindingSiteState :=
  let counts : List (BindingSiteState × Nat) :=
    [ (.Phi,    residues.countP (·.state == .Phi))
    , (.Lambda, residues.countP (·.state == .Lambda))
    , (.Rho,    residues.countP (·.state == .Rho))
    , (.Kappa,  residues.countP (·.state == .Kappa))
    , (.Omega,  residues.countP (·.state == .Omega))
    , (.Sigma,  residues.countP (·.state == .Sigma))
    , (.Pi,     residues.countP (·.state == .Pi))
    , (.Zeta,   residues.countP (·.state == .Zeta))
    ]
  (counts.foldl (fun (best : BindingSiteState × Nat) pair =>
    if pair.2 > best.2 then pair else best) (.Zeta, 0)).1

-- ═══════════════════════════════════════════════════════════════════════════
-- §4  Profile builder
-- ═══════════════════════════════════════════════════════════════════════════

/-- Build a BindingSiteProfile from a list of classified residues.
    All arithmetic is Q16_16; no Float. -/
def buildProfile
    (classified : List (AminoAcidToken × Q16_16 × BindingSiteState))
    : BindingSiteProfile :=
  let entropies := classified.map (·.2.1)
  let dominant  := dominantState (classified.map fun (t, e, s) =>
    { token := t, entropy := e, state := s
    , position := (Q16_16.zero, Q16_16.zero, Q16_16.zero)
    , bindability := Q16_16.ofNat 50 })
  { residues     := classified.map fun (t, e, s) =>
      { token := t, entropy := e, state := s
      , position    := (Q16_16.zero, Q16_16.zero, Q16_16.zero)
      , bindability := Q16_16.ofNat 50 }
  , totalEntropy := q16Mean entropies
  , maxEntropy   := q16Max  entropies
  , minEntropy   := q16Min  entropies
  , siteState    := dominant
  , druggable    := dominant == .Pi || dominant == .Lambda
  , receiptHash  := "PENDING" }

end BindingSite
