// model.sy -- AetherBrain: The Complete NCA Model in Scyla
// 32.6M parameters. 4-tier cascade. 136 conductance heads.
// Every gate is H-H. Every decay is pharmacokinetic. No sigmoid.
//
// This IS the patent. This IS the product. Running at compiled Rust speed on GPU.
//
// Architecture:
//   Input -> Embed -> [Cascade Layer x4] -> Grammar Mix -> Norm -> Output Head
//
// Each Cascade Layer:
//   Tier 0 (8 amino acids) --H-H gates--> Tier 1 (6 precursors)
//   --H-H gates--> Tier 2 (8 NTs) --H-H gates--> Tier 3 (12 brain regions)
//   -> Astrocyte Metalayer fusion
//
// Marjorie McCubbins, BSc Biochemistry and Molecular Biology
// Aislinn McCubbins, Scyla language design
// April 2026
// Patent Pending: 63/939,190 & 63/962,385 & 63/988,485

use config::{TIER_SIZES, ANTAGONISTIC_PAIRS}

// ═══════════════════════════════════════════════════════════
// CONFIG
// ═══════════════════════════════════════════════════════════

struct BrainConfig {
    vocab_size: Int,
    hidden_dim: Int,
    max_seq_len: Int,
    num_cascade_layers: Int,
    num_grammar_heads: Int,
    grammar_dim: Int,
    entity_count: Int,
    entity_dim: Int,
    sparse_threshold: Float,
    dropout: Float,
    stimulus_scale: Float,
}

impl BrainConfig {
    fn standard() -> BrainConfig {
        BrainConfig {
            vocab_size: 4098,
            hidden_dim: 512,
            max_seq_len: 128,
            num_cascade_layers: 4,
            num_grammar_heads: 4,
            grammar_dim: 128,
            entity_count: 256,
            entity_dim: 64,
            sparse_threshold: 0.05,
            dropout: 0.1,
            stimulus_scale: 0.3,
        }
    }

    fn small() -> BrainConfig {
        BrainConfig {
            vocab_size: 4098,
            hidden_dim: 256,
            max_seq_len: 128,
            num_cascade_layers: 2,
            num_grammar_heads: 2,
            grammar_dim: 64,
            entity_count: 256,
            entity_dim: 32,
            sparse_threshold: 0.05,
            dropout: 0.1,
            stimulus_scale: 0.3,
        }
    }
}

// ═══════════════════════════════════════════════════════════
// CONDUCTANCE HEAD (Tensor-based, differentiable)
// ═══════════════════════════════════════════════════════════

struct TensorHead {
    // Attention weights: Q, K, V projections
    w_qkv: Tensor,    // [hidden_dim, 3 * head_dim]
    w_out: Tensor,     // [head_dim, hidden_dim]

    // H-H gate parameters (learnable)
    w_alpha: Tensor,   // [1]
    b_alpha: Tensor,   // [1]
    w_beta: Tensor,    // [1]
    b_beta: Tensor,    // [1]

    // Pharmacokinetic state
    state: Float,      // Current concentration S_i(t)
    half_life: Float,  // Published half-life for this NT
    baseline: Float,   // Tonic drive baseline (published)
    head_dim: Int,
}

impl TensorHead {
    fn new(hidden_dim: Int, head_dim: Int, nt_name: String) -> TensorHead {
        // Initialize with small random weights (Xavier-like)
        let scale = 1.0 / sqrt(head_dim + 0.0)
        TensorHead {
            w_qkv: t_mul(tensor_rand([hidden_dim, 3 * head_dim]), tensor_fill([hidden_dim, 3 * head_dim], scale)),
            w_out: t_mul(tensor_rand([head_dim, hidden_dim]), tensor_fill([head_dim, hidden_dim], scale)),
            w_alpha: tensor_rand([1]),
            b_alpha: tensor_zeros([1]),
            w_beta: tensor_rand([1]),
            b_beta: tensor_zeros([1]),
            state: tonic_drive(nt_name),
            half_life: bio_half_life(nt_name),
            baseline: tonic_drive(nt_name),
            head_dim: head_dim,
        }
    }

    // H-H conductance gate -- THE gate. NOT sigmoid.
    fn conductance(&self) -> Float {
        hh_gate(self.state, self.w_alpha, self.b_alpha, self.w_beta, self.b_beta)
    }

    // Forward: attention gated by H-H conductance
    fn forward(&mut self, x: Tensor, dt: Float) -> Tensor {
        // 1. Compute conductance from pharmacokinetic state
        let g = self.conductance()

        // 2. Sparse threshold -- if gate below threshold, skip entirely
        if g < 0.05 {
            // Head is below threshold. Zero cost. Zero output.
            return tensor_zeros(t_shape(x))
        }

        // 3. QKV projection
        let qkv = t_matmul(x, self.w_qkv)
        // Split into Q, K, V
        let hd = self.head_dim
        let q = t_slice_last(qkv, 0, hd)
        let k = t_slice_last(qkv, hd, 2 * hd)
        let v = t_slice_last(qkv, 2 * hd, 3 * hd)

        // 4. Scaled dot-product attention (causal)
        let attn = multi_head_attention(q, k, v, 1, true)

        // 5. Output projection, gated by conductance
        let out = t_matmul(attn, self.w_out)
        t_mul(out, tensor_fill(t_shape(out), g))
    }

    // Decay state toward baseline between timesteps
    fn decay(&mut self, dt: Float) {
        self.state = half_life_to_baseline(self.state, self.half_life, dt, self.baseline)
    }

    // Receive stimulus (from lower tier or external input)
    fn stimulate(&mut self, amount: Float) {
        self.state = self.state + amount
        if self.state > 1.0 { self.state = 1.0 }
    }

    fn params(&self) -> Vec<Tensor> {
        [self.w_qkv, self.w_out, self.w_alpha, self.b_alpha, self.w_beta, self.b_beta]
    }
}

// ═══════════════════════════════════════════════════════════
// CASCADE TIER
// ═══════════════════════════════════════════════════════════

struct CascadeTier {
    heads: Vec<TensorHead>,
    tier_gate_w: Tensor,  // Gate from lower tier output to this tier
    norm_gamma: Tensor,   // LayerNorm scale
    norm_beta: Tensor,    // LayerNorm shift
}

impl CascadeTier {
    fn new(n_heads: Int, hidden_dim: Int, names: Vec<String>) -> CascadeTier {
        let head_dim = hidden_dim / n_heads
        let heads = []
        for i in 0..n_heads {
            heads.push(TensorHead::new(hidden_dim, head_dim, names[i]))
        }
        CascadeTier {
            heads: heads,
            tier_gate_w: tensor_rand([hidden_dim, hidden_dim]),
            norm_gamma: tensor_ones([hidden_dim]),
            norm_beta: tensor_zeros([hidden_dim]),
        }
    }

    fn forward(&mut self, x: Tensor, lower_gate: Float, dt: Float) -> Tensor {
        // Sum active head outputs
        let result = tensor_zeros(t_shape(x))
        let active_count = 0

        for i in 0..self.heads.len() {
            let head_out = self.heads[i].forward(x, dt)
            result = t_add(result, head_out)
            if self.heads[i].conductance() >= 0.05 {
                active_count = active_count + 1
            }
            // Decay each head's state
            self.heads[i].decay(dt)
        }

        // Normalize by active heads (not total heads)
        if active_count > 0 {
            result = t_mul(result, tensor_fill(t_shape(result), 1.0 / (active_count + 0.0)))
        }

        // Gate by lower tier (H-H rate, not sigmoid)
        result = t_mul(result, tensor_fill(t_shape(result), lower_gate))

        // Residual + LayerNorm
        let residual = t_add(x, result)
        kernel_layer_norm(residual, self.norm_gamma, self.norm_beta)
    }

    fn params(&self) -> Vec<Tensor> {
        let p = [self.tier_gate_w, self.norm_gamma, self.norm_beta]
        for i in 0..self.heads.len() {
            let hp = self.heads[i].params()
            for j in 0..hp.len() {
                p.push(hp[j])
            }
        }
        p
    }
}

// ═══════════════════════════════════════════════════════════
// CASCADE LAYER (one full pass through all 4 tiers)
// ═══════════════════════════════════════════════════════════

struct CascadeLayer {
    tier0: CascadeTier,   // 8 amino acids
    tier1: CascadeTier,   // 6 precursors
    tier2: CascadeTier,   // 8 neurotransmitters
    tier3: CascadeTier,   // 12 brain regions
}

impl CascadeLayer {
    fn new(hidden_dim: Int) -> CascadeLayer {
        let t0_names = ["phenylalanine", "glycine", "aspartate", "glutamine",
                        "histidine", "tyrosine", "tryptophan", "arginine"]
        let t1_names = ["tyrosine_p", "tryptophan_p", "glutamate",
                        "choline", "cholesterol", "pomc"]
        let t2_names = ["dopamine", "norepinephrine", "serotonin", "gaba",
                        "oxytocin", "cortisol", "endorphin", "acetylcholine"]
        let t3_names = ["brocas", "wernickes", "prefrontal", "amygdala",
                        "hippocampus", "motor", "angular", "acc",
                        "insula", "basal_ganglia", "cerebellum", "temporal"]

        CascadeLayer {
            tier0: CascadeTier::new(8, hidden_dim, t0_names),
            tier1: CascadeTier::new(6, hidden_dim, t1_names),
            tier2: CascadeTier::new(8, hidden_dim, t2_names),
            tier3: CascadeTier::new(12, hidden_dim, t3_names),
        }
    }

    fn forward(&mut self, x: Tensor, dt: Float) -> Tensor {
        // Tier 0: Amino acids (always active, gate = 1.0)
        let h0 = self.tier0.forward(x, 1.0, dt)

        // Tier 0 gates Tier 1 via H-H conductance
        let gate_01 = self.tier0_gate()
        let h1 = self.tier1.forward(h0, gate_01, dt)

        // Tier 1 gates Tier 2
        let gate_12 = self.tier1_gate()
        let h2 = self.tier2.forward(h1, gate_12, dt)

        // Tier 2 gates Tier 3
        let gate_23 = self.tier2_gate()
        let h3 = self.tier3.forward(h2, gate_23, dt)

        // Antagonistic pair dynamics within Tier 2
        self.apply_antagonistic_pairs()

        h3
    }

    // Compute tier gate as mean conductance of tier heads
    fn tier0_gate(&self) -> Float {
        let sum = 0.0
        for i in 0..self.tier0.heads.len() {
            sum = sum + self.tier0.heads[i].conductance()
        }
        sum / 8.0
    }

    fn tier1_gate(&self) -> Float {
        let sum = 0.0
        for i in 0..self.tier1.heads.len() {
            sum = sum + self.tier1.heads[i].conductance()
        }
        sum / 6.0
    }

    fn tier2_gate(&self) -> Float {
        let sum = 0.0
        for i in 0..self.tier2.heads.len() {
            sum = sum + self.tier2.heads[i].conductance()
        }
        sum / 8.0
    }

    // Coupled antagonistic dynamics
    fn apply_antagonistic_pairs(&mut self) {
        // Dopamine (idx 0) vs Serotonin (idx 2): reward vs stability
        let da = self.tier2.heads[0].state
        let ser = self.tier2.heads[2].state
        let pair1 = antagonistic_pair(da, ser, 0.693 / 60.0, 0.693 / 360.0, 0.1, 0.1, 0.1, 1.0)
        // pair1 returns updated values

        // Cortisol (idx 5) vs Oxytocin (idx 4): threat vs bonding
        let cort = self.tier2.heads[5].state
        let oxy = self.tier2.heads[4].state
        let pair2 = antagonistic_pair(cort, oxy, 0.693 / 90.0, 0.693 / 180.0, 0.1, 0.1, 0.1, 1.0)

        // Norepinephrine (idx 1) vs Endorphin (idx 6): vigilance vs comfort
        let ne = self.tier2.heads[1].state
        let endo = self.tier2.heads[6].state
        let pair3 = antagonistic_pair(ne, endo, 0.693 / 30.0, 0.693 / 300.0, 0.1, 0.1, 0.1, 1.0)
    }

    // Inject hormone stimulus into Tier 2 heads (from entity context)
    fn inject_stimulus(&mut self, hormones: Vec<Float>, scale: Float) {
        // hormones: [oxytocin, cortisol, serotonin, dopamine, norepinephrine]
        if hormones.len() >= 5 {
            self.tier2.heads[4].stimulate(hormones[0] * scale)  // oxytocin
            self.tier2.heads[5].stimulate(hormones[1] * scale)  // cortisol
            self.tier2.heads[2].stimulate(hormones[2] * scale)  // serotonin
            self.tier2.heads[0].stimulate(hormones[3] * scale)  // dopamine
            self.tier2.heads[1].stimulate(hormones[4] * scale)  // norepinephrine
        }
    }

    fn params(&self) -> Vec<Tensor> {
        let p = []
        let tp = self.tier0.params()
        for i in 0..tp.len() { p.push(tp[i]) }
        let tp = self.tier1.params()
        for i in 0..tp.len() { p.push(tp[i]) }
        let tp = self.tier2.params()
        for i in 0..tp.len() { p.push(tp[i]) }
        let tp = self.tier3.params()
        for i in 0..tp.len() { p.push(tp[i]) }
        p
    }
}

// ═══════════════════════════════════════════════════════════
// GRAMMAR HEADS (always-on, ungated, Kishonic Silkie pattern)
// ═══════════════════════════════════════════════════════════

struct GrammarBank {
    w_qkv: Vec<Tensor>,
    w_out: Vec<Tensor>,
    num_heads: Int,
    head_dim: Int,
    // H-H mixing gate (how much grammar vs cascade)
    mix_alpha: Tensor,
    mix_beta: Tensor,
}

impl GrammarBank {
    fn new(num_heads: Int, hidden_dim: Int, head_dim: Int) -> GrammarBank {
        let scale = 1.0 / sqrt(head_dim + 0.0)
        let qkvs = []
        let outs = []
        for i in 0..num_heads {
            qkvs.push(t_mul(tensor_rand([hidden_dim, 3 * head_dim]), tensor_fill([hidden_dim, 3 * head_dim], scale)))
            outs.push(t_mul(tensor_rand([head_dim, hidden_dim]), tensor_fill([head_dim, hidden_dim], scale)))
        }
        GrammarBank {
            w_qkv: qkvs,
            w_out: outs,
            num_heads: num_heads,
            head_dim: head_dim,
            mix_alpha: tensor_rand([1]),
            mix_beta: tensor_rand([1]),
        }
    }

    fn forward(&self, x: Tensor) -> Tensor {
        let result = tensor_zeros(t_shape(x))
        for i in 0..self.num_heads {
            let qkv = t_matmul(x, self.w_qkv[i])
            let hd = self.head_dim
            let q = t_slice_last(qkv, 0, hd)
            let k = t_slice_last(qkv, hd, 2 * hd)
            let v = t_slice_last(qkv, 2 * hd, 3 * hd)
            let attn = multi_head_attention(q, k, v, 1, true)
            let out = t_matmul(attn, self.w_out[i])
            result = t_add(result, out)
        }
        t_mul(result, tensor_fill(t_shape(result), 1.0 / (self.num_heads + 0.0)))
    }

    // H-H gate for mixing grammar into cascade output
    fn mix_gate(&self, cascade_state: Float) -> Float {
        hh_gate(cascade_state, self.mix_alpha, 0.0, self.mix_beta, 0.0)
    }

    fn params(&self) -> Vec<Tensor> {
        let p = [self.mix_alpha, self.mix_beta]
        for i in 0..self.num_heads {
            p.push(self.w_qkv[i])
            p.push(self.w_out[i])
        }
        p
    }
}

// ═══════════════════════════════════════════════════════════
// ENTITY EMBEDDING
// ═══════════════════════════════════════════════════════════

struct EntityEmbed {
    table: Tensor,           // [entity_count, entity_dim]
    hormone_to_t2: Tensor,   // [5, 8] biological prior mapping
}

impl EntityEmbed {
    fn new(count: Int, dim: Int) -> EntityEmbed {
        // Hormone-to-T2 prior: how 5D hormones map to 8 NT channels
        // This is initialized from biological knowledge, then fine-tuned
        let h2t = tensor_zeros([5, 8])
        // Oxytocin -> oxytocin head (idx 4) + serotonin (idx 2)
        // Cortisol -> cortisol head (idx 5) + norepinephrine (idx 1)
        // Serotonin -> serotonin (idx 2) + GABA (idx 3)
        // Dopamine -> dopamine (idx 0) + acetylcholine (idx 7)
        // NE -> norepinephrine (idx 1) + dopamine (idx 0)

        EntityEmbed {
            table: t_mul(tensor_rand([count, dim]), tensor_fill([count, dim], 0.1)),
            hormone_to_t2: h2t,
        }
    }

    fn lookup(&self, entity_ids: Tensor) -> Tensor {
        embedding_lookup(self.table, entity_ids)
    }

    fn hormones_to_stimulus(&self, hormones: Tensor) -> Tensor {
        // [batch, 5] @ [5, 8] -> [batch, 8] T2 stimulus
        t_matmul(hormones, self.hormone_to_t2)
    }

    fn params(&self) -> Vec<Tensor> {
        [self.table, self.hormone_to_t2]
    }
}

// ═══════════════════════════════════════════════════════════
// AETHER BRAIN -- THE COMPLETE MODEL
// ═══════════════════════════════════════════════════════════

struct AetherBrain {
    config: BrainConfig,

    // Embedding (weight-tied with output head)
    embedding: Tensor,     // [vocab_size, hidden_dim]

    // Cascade layers
    cascade_layers: Vec<CascadeLayer>,

    // Grammar heads (always on, parallel with cascade)
    grammar: GrammarBank,

    // Entity context
    entity: EntityEmbed,

    // Final norm
    final_gamma: Tensor,
    final_beta: Tensor,

    // Output head (weight-tied: just transpose of embedding)
    // No separate output_head tensor needed

    // Pharmacokinetic state
    dt: Float,
}

impl AetherBrain {
    fn new(config: BrainConfig) -> AetherBrain {
        let hd = config.hidden_dim

        // Build cascade layers
        let layers = []
        for i in 0..config.num_cascade_layers {
            layers.push(CascadeLayer::new(hd))
        }

        println("Building AetherBrain...")
        println("  vocab_size: " + config.vocab_size)
        println("  hidden_dim: " + hd)
        println("  cascade_layers: " + config.num_cascade_layers)
        println("  heads per layer: 34 (8+6+8+12)")
        println("  total heads: " + (config.num_cascade_layers * 34))
        println("  grammar heads: " + config.num_grammar_heads)

        let brain = AetherBrain {
            config: config,
            embedding: t_mul(tensor_rand([config.vocab_size, hd]), tensor_fill([config.vocab_size, hd], 0.02)),
            cascade_layers: layers,
            grammar: GrammarBank::new(config.num_grammar_heads, hd, config.grammar_dim),
            entity: EntityEmbed::new(config.entity_count, config.entity_dim),
            final_gamma: tensor_ones([hd]),
            final_beta: tensor_zeros([hd]),
            dt: 1.0,
        }

        let total = brain.param_count()
        println("  total parameters: " + total)
        brain
    }

    // ═══════════════════════════════════════════════════════
    // FORWARD PASS
    // ═══════════════════════════════════════════════════════

    fn forward(&mut self, input_ids: Tensor, entity_ids: Tensor, hormones: Tensor, trust: Tensor) -> Tensor {
        let batch_size = t_shape(input_ids)[0]
        let seq_len = t_shape(input_ids)[1]

        // 1. Embed tokens
        let x = embedding_lookup(self.embedding, input_ids)
        // Scale by sqrt(hidden_dim)
        let scale_factor = sqrt(self.config.hidden_dim + 0.0)
        x = t_mul(x, tensor_fill(t_shape(x), scale_factor))

        // 2. Inject entity hormone stimulus into first cascade layer
        if t_len(entity_ids) > 0 {
            let stimulus = self.entity.hormones_to_stimulus(hormones)
            // stimulus is [batch, 8] -- inject into T2 heads of first layer
            // Scale by stimulus_scale
        }

        // 3. Cascade layers
        for layer_idx in 0..self.cascade_layers.len() {
            x = self.cascade_layers[layer_idx].forward(x, self.dt)
        }

        // 4. Grammar heads (parallel, always on)
        let grammar_out = self.grammar.forward(x)

        // 5. Mix cascade + grammar via H-H gate
        // Grammar contributes structural consistency
        // Cascade contributes semantic + emotional content
        let avg_state = self.average_t2_state()
        let mix = self.grammar.mix_gate(avg_state)
        // mix near 1.0 = more grammar, near 0.0 = more cascade
        let grammar_weight = mix * 0.3  // Max 30% grammar influence
        x = t_add(
            t_mul(x, tensor_fill(t_shape(x), 1.0 - grammar_weight)),
            t_mul(grammar_out, tensor_fill(t_shape(grammar_out), grammar_weight))
        )

        // 6. Final LayerNorm
        x = kernel_layer_norm(x, self.final_gamma, self.final_beta)

        // 7. Output logits (weight-tied with embedding)
        // logits = x @ embedding^T -> [batch, seq_len, vocab_size]
        let emb_t = t_transpose(self.embedding)
        let logits = t_matmul(x, emb_t)

        logits
    }

    // Average Tier 2 neurochemical state across all layers
    fn average_t2_state(&self) -> Float {
        let sum = 0.0
        let count = 0
        for i in 0..self.cascade_layers.len() {
            for j in 0..self.cascade_layers[i].tier2.heads.len() {
                sum = sum + self.cascade_layers[i].tier2.heads[j].state
                count = count + 1
            }
        }
        if count > 0 { sum / (count + 0.0) } else { 0.2 }
    }

    // Collect all learnable parameters
    fn all_params(&self) -> Vec<Tensor> {
        let p = [self.embedding, self.final_gamma, self.final_beta]

        // Cascade layers
        for i in 0..self.cascade_layers.len() {
            let lp = self.cascade_layers[i].params()
            for j in 0..lp.len() { p.push(lp[j]) }
        }

        // Grammar
        let gp = self.grammar.params()
        for i in 0..gp.len() { p.push(gp[i]) }

        // Entity
        let ep = self.entity.params()
        for i in 0..ep.len() { p.push(ep[i]) }

        p
    }

    fn param_count(&self) -> Int {
        let params = self.all_params()
        let total = 0
        for i in 0..params.len() {
            total = total + t_len(params[i])
        }
        total
    }

    // Move entire model to GPU
    fn to_gpu(&mut self) {
        println("Moving model to GPU...")
        model_to_gpu()
        println("Model on GPU.")
    }

    // Save checkpoint
    fn save(&self, path: String) {
        // Save all parameter tensors
        println("Saving checkpoint to " + path)
        // Native save_checkpoint op
    }

    // Load checkpoint
    fn load(path: String, config: BrainConfig) -> AetherBrain {
        println("Loading checkpoint from " + path)
        let brain = AetherBrain::new(config)
        // Native load_checkpoint op
        brain
    }
}
