import Semantics.FixedPoint
import Semantics.Canon

namespace Semantics.ENE

/-! # Canonical Serialization
Binary serialization, encoding, and filtering for canonical forms.
Split from Canon.lean per swarm suggestion (USER AUTHORIZED).
-/

-- Canonical Adapter / Normalization Layer
--
-- Converts raw inputs into deterministic, semantically bounded canonical forms.
-- This layer prevents "weird machines via inputs" by:
--   1. Normalizing representation (endianness, field order, encoding)
--   2. Filtering adversarial or irrelevant structure
--   3. Certifying determinism via proof

/-- Endianness policy for canonical serialization. -/
inductive EndianPolicy
| big
| little
| host
deriving Repr, BEq

/-- Bit ordering for canonical serialization. -/
inductive BitOrder
| msb0
| lsb0
deriving Repr, BEq

/-- The canonical policy is big-endian, msb0. -/
def canonicalEndian : EndianPolicy := EndianPolicy.big

def canonicalBitOrder : BitOrder := BitOrder.msb0

/-- Kinds of fields in a canonical record. -/
inductive FieldKind
| int (bits : Nat) (signed : Bool)
| nat (bits : Nat)
| q16_16
| float64
| text
| bool
| blob (size : Nat)
deriving Repr, BEq

/-- Specification for a single field. -/
structure FieldSpec where
  name : String
  kind : FieldKind
  required : Bool := true
deriving Repr, BEq

/-- A schema defining the canonical shape of a record. -/
structure RecordSchema where
  name : String
  fields : List FieldSpec
  endian : EndianPolicy := canonicalEndian
  bitOrder : BitOrder := canonicalBitOrder
deriving Repr, BEq

/-- A value in canonical form. -/
inductive CanonicalValue
| int (i : Int) (bits : Nat) (signed : Bool)
| nat (n : Nat) (bits : Nat)
| q16_16 (q : Q16_16)
| float64 (f : Float)
| text (s : String)
| bool (b : Bool)
| blob (b : ByteArray)
deriving BEq

/-- A field with its canonical value. -/
structure CanonicalField where
  spec : FieldSpec
  value : CanonicalValue
deriving BEq

/-- The complete canonical binary representation of a record. -/
structure CanonicalBinaryForm where
  schema : RecordSchema
  fields : List CanonicalField
deriving BEq

/-- Source information tracking provenance. -/
structure SourceInfo where
  origin : String
  timestamp : UInt64
  trustLevel : Float -- TODO(lean-port): port to Q16_16
deriving Repr, BEq

/-- Errors that can occur during normalization. -/
inductive NormalizeError
| typeMismatch (expected : String) (actual : String)
| overflow (value : String) (limit : String)
| missingRequiredField (name : String)
| adversarialStructure (reason : String)
| unsupportedEncoding (details : String)
deriving Repr, BEq

abbrev NormalizeResult (α : Type) := Except NormalizeError α

/-- Source values before normalization. -/
inductive SourceValue
| int (i : Int)
| nat (n : Nat)
| q16_16 (q : Q16_16)
| float64 (f : Float)
| text (s : String)
| bool (b : Bool)
| blob (b : ByteArray)
| null
deriving BEq

/-- A field in its source form. -/
structure SourceField where
  name : String
  value : SourceValue
deriving BEq

-- Serialization helpers

def pushByte (out : ByteArray) (b : UInt8) : ByteArray := out.push b

def encodeU16BE (x : UInt16) : ByteArray :=
  let b0 := UInt8.ofNat ((x.toNat >>> 8) &&& 0xFF)
  let b1 := UInt8.ofNat (x.toNat &&& 0xFF)
  ByteArray.empty.push b0 |>.push b1

def encodeU32BE (x : UInt32) : ByteArray :=
  let b0 := UInt8.ofNat ((x.toNat >>> 24) &&& 0xFF)
  let b1 := UInt8.ofNat ((x.toNat >>> 16) &&& 0xFF)
  let b2 := UInt8.ofNat ((x.toNat >>> 8) &&& 0xFF)
  let b3 := UInt8.ofNat (x.toNat &&& 0xFF)
  ByteArray.empty.push b0 |>.push b1 |>.push b2 |>.push b3

def encodeU64BE (x : UInt64) : ByteArray :=
  let b0 := UInt8.ofNat ((x.toNat >>> 56) &&& 0xFF)
  let b1 := UInt8.ofNat ((x.toNat >>> 48) &&& 0xFF)
  let b2 := UInt8.ofNat ((x.toNat >>> 40) &&& 0xFF)
  let b3 := UInt8.ofNat ((x.toNat >>> 32) &&& 0xFF)
  let b4 := UInt8.ofNat ((x.toNat >>> 24) &&& 0xFF)
  let b5 := UInt8.ofNat ((x.toNat >>> 16) &&& 0xFF)
  let b6 := UInt8.ofNat ((x.toNat >>> 8) &&& 0xFF)
  let b7 := UInt8.ofNat (x.toNat &&& 0xFF)
  ByteArray.empty.push b0 |>.push b1 |>.push b2 |>.push b3 |>.push b4 |>.push b5 |>.push b6 |>.push b7

def encodeNatBE (width : Nat) (n : Nat) : ByteArray :=
  let rec loop (i : Nat) (acc : ByteArray) : ByteArray :=
    match i with
    | 0 => acc
    | i' + 1 =>
        let byte := UInt8.ofNat ((n >>> (8 * i')) &&& 0xFF)
        loop i' (acc.push byte)
  loop width ByteArray.empty

def encodeText (s : String) : ByteArray :=
  s.toUTF8

def fieldKindTag (k : FieldKind) : UInt8 :=
  match k with
  | FieldKind.int _ _ => 1
  | FieldKind.nat _   => 2
  | FieldKind.q16_16  => 3
  | FieldKind.float64 => 4
  | FieldKind.text    => 5
  | FieldKind.bool    => 6
  | FieldKind.blob _  => 7

def intFitsSigned (bits : Nat) (i : Int) : Bool :=
  let limit := 1 <<< (bits - 1)
  i ≥ -limit && i < limit

def serializeCanonicalValue (v : CanonicalValue) : NormalizeResult ByteArray :=
  match v with
  | CanonicalValue.int i bits signed =>
      if signed then
        if intFitsSigned bits i then
          .ok (encodeNatBE bits (if i < 0 then (1 <<< bits) + i.toNat else i.toNat))
        else
          .error (NormalizeError.overflow (toString i) ("int" ++ toString bits))
      else
        if i ≥ 0 && i < (1 <<< bits : Int) then
          .ok (encodeNatBE bits i.toNat)
        else
          .error (NormalizeError.overflow (toString i) ("uint" ++ toString bits))
  | CanonicalValue.nat n bits =>
      if n < (1 <<< bits) then
        .ok (encodeNatBE bits n)
      else
        .error (NormalizeError.overflow (toString n) ("uint" ++ toString bits))
  | CanonicalValue.q16_16 q =>
      .ok (encodeU32BE q.val)
  | CanonicalValue.float64 f =>
      .ok (encodeU64BE (Float.toUInt64 f))
  | CanonicalValue.text s =>
      .ok (encodeText s)
  | CanonicalValue.bool true =>
      .ok (ByteArray.empty.push 1)
  | CanonicalValue.bool false =>
      .ok (ByteArray.empty.push 0)
  | CanonicalValue.blob b =>
      .ok b

def serializeCanonicalField (f : CanonicalField) : NormalizeResult ByteArray := do
  let tagBytes := ByteArray.empty.push (fieldKindTag f.spec.kind)
  let nameBytes := encodeText f.spec.name
  let valueBytes ← serializeCanonicalValue f.value
  pure (tagBytes ++ nameBytes ++ valueBytes)

def serializeCanonicalBinaryForm (cbf : CanonicalBinaryForm) : NormalizeResult ByteArray := do
  let schemaBytes := encodeText cbf.schema.name
  let rec serializeFields (fields : List CanonicalField) (acc : ByteArray) : NormalizeResult ByteArray :=
    match fields with
    | [] => pure acc
    | f :: rest => do
        let fieldBytes ← serializeCanonicalField f
        serializeFields rest (acc ++ fieldBytes)
  let fieldBytes ← serializeFields cbf.fields ByteArray.empty
  pure (schemaBytes ++ fieldBytes)

def fieldKindCoreSafe (k : FieldKind) : Bool :=
  match k with
  | FieldKind.int bits _signed => bits ≤ 64
  | FieldKind.nat bits => bits ≤ 64
  | FieldKind.q16_16 => true
  | FieldKind.float64 => true
  | FieldKind.text => true
  | FieldKind.bool => true
  | FieldKind.blob size => size ≤ 65536

def uniqueFieldNames : List FieldSpec → Bool
| [] => true
| f :: rest =>
    !rest.any (fun g => g.name == f.name) && uniqueFieldNames rest

/-- A schema is core-admissible when it is deterministic, canonical, and fixed-point-safe. -/
def RecordSchema.coreAdmissible (schema : RecordSchema) : Bool :=
  schema.endian == canonicalEndian &&
  schema.bitOrder == canonicalBitOrder &&
  uniqueFieldNames schema.fields &&
  schema.fields.all (fun field => field.name != "" && fieldKindCoreSafe field.kind)

/-- `q16_16` is accepted by the core-safe schema checker. -/
theorem q16_16_field_kind_core_safe :
  fieldKindCoreSafe FieldKind.q16_16 = true := by
  native_decide

-- Determinism and identity theorems

/-- Two canonical binary forms have the same identity if their schemas and
serialized bytes are equal. -/
def SameIdentity (a b : CanonicalBinaryForm) : Prop :=
  a.schema = b.schema ∧
  (∀ ha hb, serializeCanonicalBinaryForm a = .ok ha → serializeCanonicalBinaryForm b = .ok hb → ha = hb)

/-- A canonical binary form is canonical if it serializes deterministically. -/
def IsCanonical (cbf : CanonicalBinaryForm) : Prop :=
  ∀ h1 h2, serializeCanonicalBinaryForm cbf = .ok h1 → serializeCanonicalBinaryForm cbf = .ok h2 → h1 = h2

-- Serialization of a given schema and source is deterministic:
-- the same input always produces the same canonical bytes.
-- COMMENTED OUT: References undefined canonicalize function.
-- TODO(lean-port): Implement canonicalize function or remove this theorem.
-- theorem canonicalize_is_deterministic
--   (schema : RecordSchema)
--   (src : List SourceField)
--   (cbf : CanonicalBinaryForm)
--   (_h : canonicalize schema src = .ok cbf) :
--   IsCanonical cbf := by
--   unfold IsCanonical
--   intros h1 h2 e1 e2
--   have heq : h1 = h2 := by
--     have h : @Except.ok NormalizeError ByteArray h1 = @Except.ok NormalizeError ByteArray h2 := by
--       rw [← e1, ← e2]
--     injection h
--   exact heq

-- Filtering for adversarial / irrelevant structure

/-- Relevance classification for source fields. -/
inductive Relevance
| relevant      -- Maps directly to a semantic atom
| structural    -- Needed for parsing but not meaning
| metadata      -- Provenance, not content
| noise         -- Does not contribute to meaning
| adversarial   -- Attempts to induce unintended computation
deriving Repr, BEq

/-- A filtered field carries a relevance judgment. -/
structure FilteredField where
  field : SourceField
  relevance : Relevance
  reason : String
deriving BEq

/-- Result of filtering a source record. -/
structure FilterResult where
  kept : List FilteredField
  dropped : List FilteredField
  safe : Bool -- true if no adversarial fields detected
deriving BEq

/-- A filtering rule assigns relevance to a source field. -/
structure FilterRule where
  name : String
  predicate : SourceField → Bool
  relevance : Relevance
  reason : String

/-- Apply a list of filter rules to source fields. -/
def applyFilters (rules : List FilterRule) (src : List SourceField) : FilterResult :=
  let results := src.map (λ f =>
    match rules.find? (λ r => r.predicate f) with
    | some r => { field := f, relevance := r.relevance, reason := r.reason }
    | none   => { field := f, relevance := Relevance.relevant, reason := "default" }
  )
  {
    kept := results.filter (λ r => r.relevance != Relevance.noise && r.relevance != Relevance.adversarial),
    dropped := results.filter (λ r => r.relevance == Relevance.noise || r.relevance == Relevance.adversarial),
    safe := !(results.any (λ r => r.relevance == Relevance.adversarial))
  }

-- If filtering marks everything safe, then no kept field is adversarial.
-- COMMENTED OUT: Contains proof placeholder - requires proof.
-- TODO(lean-port): Re-enable when proof is completed.
-- theorem filter_safe_no_adversarial_kept
--   (rules : List FilterRule)
--   (src : List SourceField)
--   (_h : (applyFilters rules src).safe = true) :
--   ∀ r ∈ (applyFilters rules src).kept, r.relevance != Relevance.adversarial := by
--   unfold applyFilters
--   intro r hr

end Semantics.ENE
