// stdlib/crypto/cardea.sy — Living DNA-Lattice Encryption
//
// Cardea (Roman goddess of door hinges) — a cryptographic key as a
// temporal biological process. The key isn't a file on disk. It's a
// state that mutates every tick via:
//   1. Layer 0 constitutional approval
//   2. Hodgkin-Huxley gated mutation timing
//   3. Entropy harvesting from /dev/urandom + cascade state
//   4. DNA point mutation driven by entropy
//   5. Kyber-768 keypair re-derivation from DNA+entropy seed
//   6. Chain-of-custody hashing for generation history
//
// PHASE 2 — real Kyber-768 (FIPS 203 ML-KEM) via the ml-kem crate.
// The lattice layer is now true post-quantum security, not a placeholder.
// The temporal evolution architecture is unchanged from Phase 1 — only
// the lattice primitive was swapped. The compiler still enforces that
// state fields can ONLY mutate inside evolve().
//
// Byte sizes (from FIPS 203 ML-KEM-768):
//   encapsulation key:  1184 bytes
//   decapsulation key:  2400 bytes
//   ciphertext:         1088 bytes
//   shared secret:        32 bytes
//
// Aislinn McCubbins + Marjorie McCubbins, April 2026
// Co-created with Claude. Scyla-exclusive. Patent pending.

use crypto::dna

// ── Constants ─────────────────────────────────────────────

fn cardea_archive_depth() -> Int { return 16 }
fn cardea_ek_size() -> Int { return 1184 }    // FIPS 203 ML-KEM-768
fn cardea_dk_size() -> Int { return 2400 }
fn cardea_ct_size() -> Int { return 1088 }
fn cardea_ss_size() -> Int { return 32 }
fn cardea_mutation_rate() -> Float { return 0.02 }
fn cardea_hh_threshold() -> Float { return 0.15 }

// ── Helper: Hodgkin-Huxley gate component functions ──────

// Standard HH activation (m) steady-state as a function of voltage
fn hh_m_inf(v: Float) -> Float {
    // Sigmoid shape: 0 at low v, 1 at high v
    let x = 0.0 - v
    let e = exp(x)
    return 1.0 / (1.0 + e)
}

// HH inactivation (h) steady-state: high at rest, drops at high v
fn hh_h_inf(v: Float) -> Float {
    let x = v - 0.5
    let e = exp(x)
    return 1.0 / (1.0 + e)
}

// Activation time constant (fast, ~1 tick)
fn hh_tau_m(v: Float) -> Float {
    return 1.0
}

// Inactivation time constant (slow, ~10 ticks)
fn hh_tau_h(v: Float) -> Float {
    return 10.0
}

// Entropy pressure drives the mutation channel's voltage.
// Higher accumulated entropy = more pressure = gate opens sooner.
fn entropy_pressure(pool: Tensor, cascade: Tensor) -> Float {
    // Entropy pressure drives the HH voltage. Scaled to [0, ~2] so the
    // gate dynamics operate in the interesting region where both m (activation)
    // and h (inactivation) are partially open, rather than saturating at v>>1.
    let pool_energy = tensor_sum(pool) / (256.0 * 64.0)
    let cascade_energy = tensor_sum(cascade) / (34.0 * 34.0)
    return (pool_energy * 0.7 + cascade_energy * 0.3) * 2.0
}

// XOR-fold: combine current entropy pool with fresh bytes + cascade bytes
fn xor_fold(pool: Tensor, fresh: Tensor, cascade_bytes: Tensor) -> Tensor {
    let step1 = xor_bytes(pool, fresh)
    let step2 = xor_bytes(step1, cascade_bytes)
    return step2
}

// Convert a float tensor (cascade state) to byte tensor (0-255 range)
// by scaling and clamping each element
fn tensor_to_bytes(t: Tensor) -> Tensor {
    let n = tensor_len(t)
    let mut out = t_zeros(n)
    let mut i = 0
    while i < n {
        let v = t_index_get(t, i)
        // Scale from [-1, 1] to [0, 255]
        let mut scaled = (v + 1.0) * 127.5
        if scaled < 0.0 { scaled = 0.0 }
        if scaled > 255.0 { scaled = 255.0 }
        out = t_index_set(out, i, scaled)
        i = i + 1
    }
    return out
}

// Serialize Int generation counter into 4 LE bytes
fn gen_to_bytes(generation: Int) -> Tensor {
    let g = generation
    let b0 = g - ((g / 256) * 256)
    let g1 = g / 256
    let b1 = g1 - ((g1 / 256) * 256)
    let g2 = g1 / 256
    let b2 = g2 - ((g2 / 256) * 256)
    let b3 = g2 / 256
    let mut out = t_zeros(4)
    out = t_index_set(out, 0, b0 + 0.0)
    out = t_index_set(out, 1, b1 + 0.0)
    out = t_index_set(out, 2, b2 + 0.0)
    out = t_index_set(out, 3, b3 + 0.0)
    return out
}

// ── Layer 0 constitutional check ─────────────────────────

// Phase 2.5: wire into real layer0.sy. For now, always allow.
// Reserved as an extension point for Mom's constitutional substrate.
fn layer0_check_operation(op_name: String, generation: Int) -> Bool {
    // TODO Phase 2.5: call into stdlib/layer0.sy for real constitutional verdict
    return true
}

// ── Keypair Re-Derivation (the "living" mutation) ───────

// Build a 64-byte deterministic seed from (dna || entropy || parent_hash),
// then call real Kyber-768 generate_deterministic(d, z) where d=seed[0..32],
// z=seed[32..64]. Returns [ek_tensor, dk_tensor].
//
// The same DNA + entropy + parent_hash always produces the same keypair.
// That's what makes LivingKey trajectories reproducible for audit and
// archive recovery.
fn rederive_keypair(dna: Array, entropy: Tensor, parent_hash: Tensor) -> Array {
    // Combine: dna bytes || entropy pool || parent hash
    let dna_bytes = dna_to_bytes(dna)
    let combined_1 = t_cat(dna_bytes, entropy)
    let combined_2 = t_cat(combined_1, parent_hash)

    // Normalize to exactly 64 bytes of seed material via SHAKE-256
    let seed_64 = shake_256(combined_2, 64)

    // Call into the VM builtin — this is real FIPS 203 Kyber
    return kyber_keygen_from_seed(seed_64)
}

// ── The LivingKey Temporal Struct ───────────────────────

// Struct fields cannot have trailing comments, so layout notes live here:
//   lattice_pk: Tensor[1184]   — FIPS 203 ML-KEM-768 encapsulation key bytes
//   lattice_sk: Tensor[2400]   — decapsulation key bytes
//   dna: Array                 — 256 nucleotides
//   entropy_pool: Tensor[64]   — accumulated entropy
//   parent_hash: Tensor[32]    — SHA3-256 of previous generation
//   cascade_coupling: Tensor[34] — neurochemical state
//   archive_pk/sk/dna: Array   — parallel ring buffers, oldest-first,
//                                index (n - age) = keypair from age gens ago
temporal struct LivingKey {
    state lattice_pk: Tensor,
    state lattice_sk: Tensor,
    state dna: Array,
    state generation: Int,
    state entropy_pool: Tensor,
    state parent_hash: Tensor,
    state mg_m: Float,
    state mg_h: Float,
    state cascade_coupling: Tensor,
    state archive_pk: Array,
    state archive_sk: Array,
    state archive_dna: Array,
}

// THE EVOLVE IMPL — the only place state can mutate
impl Evolve for LivingKey {
    fn evolve(&mut self, dt: Float) {
        // Phase 1: Constitutional check
        let authorized = layer0_check_operation("cardea.evolve", self.generation)
        if authorized == false { return }

        // Phase 2: HH gate dynamics
        let v = entropy_pressure(self.entropy_pool, self.cascade_coupling)
        let m_inf = hh_m_inf(v)
        let h_inf = hh_h_inf(v)
        let tau_m = hh_tau_m(v)
        let tau_h = hh_tau_h(v)
        self.mg_m = self.mg_m + (m_inf - self.mg_m) * dt / tau_m
        self.mg_h = self.mg_h + (h_inf - self.mg_h) * dt / tau_h
        let gate_open = self.mg_m * self.mg_h
        if gate_open < cardea_hh_threshold() { return }

        // Phase 3: Harvest fresh entropy
        let fresh = secure_random_bytes(32)
        let cascade_bytes = tensor_to_bytes(self.cascade_coupling)
        self.entropy_pool = xor_fold(self.entropy_pool, fresh, cascade_bytes)

        // Phase 4: Archive the CURRENT state BEFORE mutating.
        // If the buffer is full (>= 16 entries), drop the oldest first.
        if array_len(self.archive_pk) >= 16 {
            self.archive_pk = array_slice(self.archive_pk, 1, array_len(self.archive_pk))
            self.archive_sk = array_slice(self.archive_sk, 1, array_len(self.archive_sk))
            self.archive_dna = array_slice(self.archive_dna, 1, array_len(self.archive_dna))
        }
        self.archive_pk = array_push(self.archive_pk, self.lattice_pk)
        self.archive_sk = array_push(self.archive_sk, self.lattice_sk)
        self.archive_dna = array_push(self.archive_dna, self.dna)

        // Phase 5: Mutate DNA
        self.dna = dna_point_mutate(self.dna, self.entropy_pool, 0.02 * gate_open)

        // Phase 6: Re-derive lattice keypair from new DNA via REAL Kyber
        let kp = rederive_keypair(self.dna, self.entropy_pool, self.parent_hash)
        self.lattice_pk = array_get(kp, 0)
        self.lattice_sk = array_get(kp, 1)

        // Phase 7: Update cascade coupling (evolve the neurochemical state too)
        self.cascade_coupling = cascade_forward(self.cascade_coupling)

        // Phase 8: Chain-of-custody hash + advance generation
        // Hash (pk || dna || generation) — binds everything to this tick
        let dna_bytes = dna_to_bytes(self.dna)
        let gen_bytes = gen_to_bytes(self.generation)
        let h_input_1 = t_cat(self.lattice_pk, dna_bytes)
        let h_input_2 = t_cat(h_input_1, gen_bytes)
        self.parent_hash = sha3_256(h_input_2)
        self.generation = self.generation + 1
    }
}

// ── Constructor ─────────────────────────────────────────

fn livingkey_new() -> LivingKey {
    // Birth: fresh 64-byte seed from getrandom, derive everything from it
    let seed = secure_random_bytes(64)
    let initial_dna = seed_to_dna(seed, 256)

    // Initial keypair via real Kyber-768 from the seed
    let kp = kyber_keygen_from_seed(seed)
    let initial_pk = array_get(kp, 0)
    let initial_sk = array_get(kp, 1)

    // Initial parent hash binds the first generation to the seed
    let initial_hash = sha3_256(seed)

    return LivingKey {
        lattice_pk: initial_pk,
        lattice_sk: initial_sk,
        dna: initial_dna,
        generation: 0,
        entropy_pool: seed,
        parent_hash: initial_hash,
        mg_m: 0.05,
        mg_h: 0.6,
        cascade_coupling: t_zeros(34),
        archive_pk: array_new(),
        archive_sk: array_new(),
        archive_dna: array_new(),
    }
}

// ── Encapsulation: encrypt a fresh shared secret with the CURRENT key ──

// Returns [ciphertext, bound_shared_secret].
// The ciphertext is sent to the recipient; the shared secret is used
// downstream (e.g. as an AES-GCM or ChaCha20 key).
// The shared secret is bound to both the Kyber encapsulation AND the
// DNA fingerprint via SHAKE-256, so recovering it requires the same
// DNA state that was present at encapsulation time.
fn livingkey_encapsulate(key: LivingKey) -> Array {
    // Real Kyber-768 encapsulate
    let result = kyber_encapsulate(key.lattice_pk)
    let ct = array_get(result, 0)
    let raw_ss = array_get(result, 1)

    // Bind with DNA fingerprint
    let dna_bytes = dna_to_bytes(key.dna)
    let dna_fp = shake_256(dna_bytes, 32)
    let combined = t_cat(raw_ss, dna_fp)
    let bound_ss = sha3_256(combined)

    let mut out = array_new()
    out = array_push(out, ct)
    out = array_push(out, bound_ss)
    return out
}

// ── Decapsulation: recover the shared secret from a ciphertext ──

// Walks back `age` generations in the archive (age=0 means current gen),
// decapsulates the ciphertext with that generation's sk, and binds with
// that generation's DNA fingerprint. Returns the 32-byte shared secret.
// Returns zeros if age is out of the archive window.
fn livingkey_decapsulate(key: LivingKey, age: Int, ct: Tensor) -> Tensor {
    // Age must be non-negative
    if age < 0 {
        return t_zeros(32)
    }

    // Age 0 means "current generation" — use live lattice_sk directly
    if age == 0 {
        let raw_ss = kyber_decapsulate(key.lattice_sk, ct)
        let dna_bytes = dna_to_bytes(key.dna)
        let dna_fp = shake_256(dna_bytes, 32)
        let combined = t_cat(raw_ss, dna_fp)
        return sha3_256(combined)
    }

    // Age >= 1 means "N generations ago" — check archive window
    let n = array_len(key.archive_sk)
    if age > n {
        // Archive window closed for this generation
        return t_zeros(32)
    }

    // archive[n - age] = the keypair from `age` generations ago
    // (the archive is oldest-first, so the most recent past is at n-1)
    let idx = n - age
    let past_sk = array_get(key.archive_sk, idx)
    let past_dna = array_get(key.archive_dna, idx)

    // Decapsulate with the archived decap key
    let raw_ss = kyber_decapsulate(past_sk, ct)

    // Bind with the DNA fingerprint FROM THAT GENERATION
    let past_dna_bytes = dna_to_bytes(past_dna)
    let past_dna_fp = shake_256(past_dna_bytes, 32)
    let combined = t_cat(raw_ss, past_dna_fp)
    return sha3_256(combined)
}

// ── Archive introspection helpers ────────────────────────

fn livingkey_archive_depth(key: LivingKey) -> Int {
    return array_len(key.archive_sk)
}

fn livingkey_oldest_reachable_gen(key: LivingKey) -> Int {
    // Returns the oldest generation still decryptable
    let n = array_len(key.archive_sk)
    if n == 0 { return key.generation }
    return key.generation - n
}
