// emotion_bridge.sy -- NCA Cascade State -> Emotion Pipeline -> T3 Modulation
// Bridges the NCA's 34-dimensional cascade state to emotion processing,
// then feeds the resulting 6D emotional coordinates back as T3 brain
// region modulation signals.
//
// Architecture:
//   cascade_state[34D] -> proj_in[34->10] -> emotion pipeline (black box)
//   -> proj_out[6->12] -> T3 additive modulation
//
// When the emotion pipeline is unavailable, the bridge operates in
// projection-only mode: 34D -> proj_in -> proj_passthrough -> 12D.
// The projections are still trainable and the module is fully differentiable.
//
// T3 modulation is ADDITIVE BIAS on brain region head states, not
// multiplicative gating (which would disrupt H-H dynamics).
//
// All gates use H-H rate functions: alpha = exp(clamp(w*x+b)),
// gate = alpha / (alpha + beta). NO sigmoid. NO tanh*0.5+0.5.
//
// Ported from Marjorie McCubbins' Python NCA (v3_emotion_bridge.py)
// Aislinn McCubbins, April 2026
//
// Patent Pending: U.S. Provisional Application Nos. 63/939,190, 63/962,385 & 63/988,485
// Copyright 2025-2026 Nexus Concordat Inc.

use cascade::{Cascade, ConductanceHead}

// ============================================================
// DIMENSION CONSTANTS
// ============================================================

const CASCADE_DIM: Int = 34
const HORMONE_DIM: Int = 10
const EMOTION_DIM: Int = 6
const T3_HEADS: Int = 12

// ============================================================
// LINEAR PROJECTION
// ============================================================
// Learned matrix multiply + bias. Replaces nn.Linear from Python.

struct LinearProjection {
    weights: Vec<Vec<Float>>,
    bias: Vec<Float>,
    in_dim: Int,
    out_dim: Int,
}

impl LinearProjection {
    fn new(in_dim: Int, out_dim: Int, bias_init: Float) -> LinearProjection {
        // Xavier uniform initialization with gain
        // For NCA: small initial weights so projections start gentle
        let scale = 0.5 / sqrt(in_dim + out_dim)

        let mut weights = []
        let mut o = 0
        while o < out_dim {
            let mut row = []
            let mut i = 0
            while i < in_dim {
                // Deterministic initialization pattern (alternating small values)
                let sign = 1.0
                if (o + i) % 2 == 1 { sign = -1.0 }
                row = row + [sign * scale * ((o * in_dim + i) % 7 + 1) / 7.0]
                i = i + 1
            }
            weights = weights + [row]
            o = o + 1
        }

        let mut bias = []
        o = 0
        while o < out_dim {
            bias = bias + [bias_init]
            o = o + 1
        }

        LinearProjection {
            weights: weights,
            bias: bias,
            in_dim: in_dim,
            out_dim: out_dim,
        }
    }

    fn forward(&self, input: Vec<Float>) -> Vec<Float> {
        let mut result = []
        let mut o = 0
        while o < self.out_dim {
            let mut sum = self.bias[o]
            let mut i = 0
            while i < self.in_dim {
                sum = sum + self.weights[o][i] * input[i]
                i = i + 1
            }
            result = result + [sum]
            o = o + 1
        }
        result
    }
}

// ============================================================
// 6D EMOTIONAL STATE
// ============================================================
// Output of the emotion pipeline: 6 coordinates in [-1, 1].
// Maps to Russell's circumplex + extensions.

struct EmotionalState6D {
    valence: Float,
    arousal: Float,
    dominance: Float,
    certainty: Float,
    social_orientation: Float,
    temporal_focus: Float,
}

impl EmotionalState6D {
    fn neutral() -> EmotionalState6D {
        EmotionalState6D {
            valence: 0.0,
            arousal: 0.0,
            dominance: 0.0,
            certainty: 0.0,
            social_orientation: 0.0,
            temporal_focus: 0.0,
        }
    }

    fn to_vec(&self) -> Vec<Float> {
        [self.valence, self.arousal, self.dominance,
         self.certainty, self.social_orientation, self.temporal_focus]
    }

    fn from_vec(v: Vec<Float>) -> EmotionalState6D {
        EmotionalState6D {
            valence: v[0],
            arousal: v[1],
            dominance: v[2],
            certainty: v[3],
            social_orientation: v[4],
            temporal_focus: v[5],
        }
    }
}

// ============================================================
// 10D HORMONE VECTOR
// ============================================================
// Projection target: 10 neurochemical concentrations in [0, 1].
// 5 antagonistic pairs (the patent's core insight).

struct HormoneVector10D {
    dopamine: Float,
    norepinephrine: Float,
    serotonin: Float,
    gaba: Float,
    oxytocin: Float,
    cortisol: Float,
    endorphin: Float,
    acetylcholine: Float,
    glutamate: Float,
    melatonin: Float,
}

impl HormoneVector10D {
    fn baseline() -> HormoneVector10D {
        HormoneVector10D {
            dopamine: 0.3,
            norepinephrine: 0.3,
            serotonin: 0.3,
            gaba: 0.3,
            oxytocin: 0.3,
            cortisol: 0.3,
            endorphin: 0.3,
            acetylcholine: 0.3,
            glutamate: 0.3,
            melatonin: 0.3,
        }
    }

    fn to_vec(&self) -> Vec<Float> {
        [self.dopamine, self.norepinephrine, self.serotonin,
         self.gaba, self.oxytocin, self.cortisol,
         self.endorphin, self.acetylcholine, self.glutamate, self.melatonin]
    }

    fn from_vec(v: Vec<Float>) -> HormoneVector10D {
        HormoneVector10D {
            dopamine: v[0],
            norepinephrine: v[1],
            serotonin: v[2],
            gaba: v[3],
            oxytocin: v[4],
            cortisol: v[5],
            endorphin: v[6],
            acetylcholine: v[7],
            glutamate: v[8],
            melatonin: v[9],
        }
    }
}

// ============================================================
// EMOTION BRIDGE
// ============================================================
// Bridge between NCA cascade state and emotion processing.
//
// cascade_state[34D] -> proj_in[34->10] -> emotion pipeline
// -> proj_out[6->12] -> T3 additive modulation
//
// All gating uses H-H rate functions. NO sigmoid. NO tanh*0.5+0.5.

temporal struct EmotionBridge {
    // === LEARNABLE PROJECTION IN: 34D cascade -> 10D hormones ===
    state proj_in: LinearProjection,

    // === LEARNABLE PROJECTION OUT: 6D emotion -> 12D T3 modulation ===
    state proj_out: LinearProjection,

    // === PROJECTION-ONLY PASSTHROUGH (when emotion pipeline unavailable) ===
    // Maps hormone_dim -> emotion_dim as a learned approximation
    state proj_passthrough: LinearProjection,

    // === H-H RATE GATE for modulation strength ===
    // Controls how much emotion output influences T3 heads.
    // alpha/(alpha+beta) gives a learned gate value.
    // Initialized for gate ~ 0.3 (gentle initial influence).
    state gate_alpha_w: Float,
    state gate_alpha_b: Float,
    state gate_beta_w: Float,
    state gate_beta_b: Float,

    // Modulation gain: scales final output to prevent disrupting cascade
    const modulation_gain: Float = 0.05,

    // Whether external emotion pipeline is available
    state pipeline_available: Bool = false,

    // Telemetry
    state last_hormones: Vec<Float>,
    state last_emotions: Vec<Float>,
    state last_modulation: Vec<Float>,
}

impl EmotionBridge {
    fn new() -> EmotionBridge {
        EmotionBridge {
            // proj_in: 34D -> 10D, bias -0.2 so baseline cascade maps to ~0.3 hormones
            proj_in: LinearProjection::new(CASCADE_DIM, HORMONE_DIM, -0.2),

            // proj_out: 6D -> 12D, bias 0.0 for neutral initial modulation
            proj_out: LinearProjection::new(EMOTION_DIM, T3_HEADS, 0.0),

            // proj_passthrough: 10D -> 6D, approximates what emotion pipeline would produce
            proj_passthrough: LinearProjection::new(HORMONE_DIM, EMOTION_DIM, 0.0),

            // H-H gate initialized for gate ~ 0.3:
            // alpha = exp(log(0.3)) = 0.3, beta = exp(log(0.7)) = 0.7
            // gate = 0.3 / (0.3 + 0.7) = 0.3
            gate_alpha_w: 0.0,
            gate_alpha_b: -1.204,   // log(0.3) ~ -1.204
            gate_beta_w: 0.0,
            gate_beta_b: -0.357,    // log(0.7) ~ -0.357

            modulation_gain: 0.05,
            pipeline_available: false,

            last_hormones: [],
            last_emotions: [],
            last_modulation: [],
        }
    }

    // H-H rate gate for modulation strength.
    // Higher cascade activity -> potentially stronger emotion influence.
    // Uses real H-H alpha/beta rate functions. NO sigmoid.
    fn compute_gate(&self, state_norm: Float) -> Float {
        let a_arg = self.gate_alpha_w * state_norm + self.gate_alpha_b
        let b_arg = self.gate_beta_w * state_norm + self.gate_beta_b

        // Clamp to [-3, 3] for numerical stability
        let a_clamped = a_arg
        if a_clamped < -3.0 { a_clamped = -3.0 }
        if a_clamped > 3.0 { a_clamped = 3.0 }
        let b_clamped = b_arg
        if b_clamped < -3.0 { b_clamped = -3.0 }
        if b_clamped > 3.0 { b_clamped = 3.0 }

        let alpha = exp(a_clamped)
        let beta = exp(b_clamped)
        alpha / (alpha + beta + 0.00000001)
    }

    // Clamp a vector's elements to [lo, hi]
    fn clamp_vec(v: Vec<Float>, lo: Float, hi: Float) -> Vec<Float> {
        let mut result = []
        let mut i = 0
        while i < v.len() {
            let val = v[i]
            if val < lo { result = result + [lo] }
            else if val > hi { result = result + [hi] }
            else { result = result + [val] }
            i = i + 1
        }
        result
    }

    // Compute mean of a vector (for state norm)
    fn vec_mean(v: Vec<Float>) -> Float {
        let mut sum = 0.0
        let mut i = 0
        while i < v.len() {
            sum = sum + v[i]
            i = i + 1
        }
        sum / (v.len() + 0.00000001)
    }

    // Compute T3 modulation signal from cascade state.
    //
    // Args:
    //   cascade_state: [34] vector of all head states
    //
    // Returns:
    //   modulation: [12] vector of additive T3 head biases
    fn forward(&mut self, cascade_state: Vec<Float>) -> Vec<Float> {
        // Project cascade state to hormone space
        // Clamp output to valid hormone range [0, 1]
        let hormones_raw = self.proj_in.forward(cascade_state)
        let hormones = EmotionBridge::clamp_vec(hormones_raw, 0.0, 1.0)

        // Emotion output: use passthrough projection (learned approximation)
        // When external emotion pipeline is available, it would replace this
        // with detached/no-grad black-box computation
        let emotions_raw = self.proj_passthrough.forward(hormones)
        let emotions = EmotionBridge::clamp_vec(emotions_raw, -1.0, 1.0)

        // Project emotions to T3 modulation signals
        let raw_modulation = self.proj_out.forward(emotions)

        // Apply H-H gate for modulation strength control
        let state_norm = EmotionBridge::vec_mean(cascade_state)
        let gate = self.compute_gate(state_norm)

        // Final modulation: gated, scaled, ADDITIVE
        let mut modulation = []
        let mut i = 0
        while i < T3_HEADS {
            modulation = modulation + [raw_modulation[i] * gate * self.modulation_gain]
            i = i + 1
        }

        // Store telemetry
        self.last_hormones = hormones
        self.last_emotions = emotions
        self.last_modulation = modulation

        modulation
    }

    // Apply the computed modulation to cascade T3 heads.
    // Modulation is ADDITIVE (bias), not multiplicative.
    fn apply_to_cascade(&mut self, cascade_state: Vec<Float>, cascade: &mut Cascade) {
        let modulation = self.forward(cascade_state)

        let mut i = 0
        while i < T3_HEADS {
            // Additive bias on brain region head concentration
            // Does not disrupt H-H dynamics, just shifts operating point
            cascade.tier_3.heads[i].stimulate(modulation[i])
            i = i + 1
        }
    }
}

impl Evolve for EmotionBridge {
    fn evolve(&mut self, dt: Duration) {
        // Projections are learned parameters, not temporally evolved.
        // The bridge itself has no intrinsic temporal dynamics.
        // All temporal behavior comes from the cascade it modulates.
    }
}

// ============================================================
// TELEMETRY
// ============================================================

struct EmotionBridgeTelemetry {
    pipeline_available: Bool,
    hormones: Vec<Float>,
    emotions: Vec<Float>,
    modulation: Vec<Float>,
    hormone_names: Vec<String>,
    emotion_names: Vec<String>,
    t3_head_names: Vec<String>,
}

impl EmotionBridge {
    fn telemetry(&self) -> EmotionBridgeTelemetry {
        EmotionBridgeTelemetry {
            pipeline_available: self.pipeline_available,
            hormones: self.last_hormones,
            emotions: self.last_emotions,
            modulation: self.last_modulation,
            hormone_names: ["dopamine", "norepinephrine", "serotonin", "gaba",
                            "oxytocin", "cortisol", "endorphin", "acetylcholine",
                            "glutamate", "melatonin"],
            emotion_names: ["valence", "arousal", "dominance",
                            "certainty", "social_orientation", "temporal_focus"],
            t3_head_names: ["prefrontal", "amygdala", "hippocampus", "insula",
                            "anterior_cingulate", "nucleus_accumbens",
                            "hypothalamus", "periaqueductal_gray",
                            "ventral_tegmental", "dorsal_raphe",
                            "locus_coeruleus", "basal_ganglia"],
        }
    }
}

// ============================================================
// CONVENIENCE: ONE-SHOT CASCADE -> T3 MODULATION
// ============================================================

fn cascade_to_modulation(cascade_state: Vec<Float>) -> Vec<Float> {
    let mut bridge = EmotionBridge::new()
    bridge.forward(cascade_state)
}

fn cascade_to_emotions(cascade_state: Vec<Float>) -> EmotionalState6D {
    let mut bridge = EmotionBridge::new()
    bridge.forward(cascade_state)
    EmotionalState6D::from_vec(bridge.last_emotions)
}
