// QuantumBridge — formal CPU→GPU transfer with DreamMemory capture and SacredSeal provenance.
//
// Maps EncodeResult → GpuQuantumContext + ParticlePhysicsParams.
// Validates before upload. Captures each packet into a DreamMemory ring buffer
// while the data still has coherent quantum encoding. Chain-linked SacredSeal
// provenance on every bridge packet for tamper-evident ordered transit.

use crate::dreamlet_catalog::{build_particle_physics_params_from_metaphor, GpuQuantumContext, ParticlePhysicsParams};
use dreamwell_attention::encoder::EncodeResult;
use dreamwell_metaphors::{
    constants::BRIDGE_SCHEMA_VERSION, EventCue, MaterializationTier, MetaphorEvaluator, MetaphorProfile,
    TagInfluenceFrame, WaveParams,
};

// ── DreamMemory Ring Buffer ───────────────────────────────────────────

/// A single captured bridge packet with provenance.
pub struct DreamMemoryEntry {
    pub frame: u64,
    pub quantum_context: GpuQuantumContext,
    pub particle_params: ParticlePhysicsParams,
    pub encode_digest: [u8; 32],
    pub bridge_digest: [u8; 32],
    pub seal_digest: [u8; 32],
    pub coherence: f32,
    pub feature_flags: u32,
    pub timestamp_ns: u64,
    pub validation_passed: bool,
}

/// Ring buffer capturing the last N bridge packets while data still has
/// coherent quantum encoding. After this point, data is GPU-bound.
/// Replay/debug/metrics access this buffer.
pub struct DreamMemory {
    packets: Vec<Option<DreamMemoryEntry>>,
    capacity: usize,
    head: usize,
    total_captured: u64,
}

impl DreamMemory {
    /// Pre-allocate ring buffer (default 120 = 2 seconds at 60fps).
    pub fn new(capacity: usize) -> Self {
        let mut packets = Vec::with_capacity(capacity);
        packets.resize_with(capacity, || None);
        Self {
            packets,
            capacity,
            head: 0,
            total_captured: 0,
        }
    }

    /// Push an entry to the ring, wrapping at capacity.
    pub fn capture(&mut self, entry: DreamMemoryEntry) {
        self.packets[self.head] = Some(entry);
        self.head = (self.head + 1) % self.capacity;
        self.total_captured += 1;
    }

    /// Last N entries in reverse chronological order.
    pub fn recent(&self, n: usize) -> Vec<&DreamMemoryEntry> {
        let n = n.min(self.total_captured as usize).min(self.capacity);
        let mut result = Vec::with_capacity(n);
        for i in 0..n {
            let idx = (self.head + self.capacity - 1 - i) % self.capacity;
            if let Some(ref entry) = self.packets[idx] {
                result.push(entry);
            }
        }
        result
    }

    /// Lookup by frame index (linear scan of ring).
    pub fn find_frame(&self, frame: u64) -> Option<&DreamMemoryEntry> {
        self.packets
            .iter()
            .filter_map(|p| p.as_ref())
            .find(|e| e.frame == frame)
    }

    /// Monotonic counter of total entries ever captured.
    pub fn total_captured(&self) -> u64 {
        self.total_captured
    }
}

// ── Bridge Packet ─────────────────────────────────────────────────────

/// Output of a bridge() call — ready for GPU upload.
pub struct BridgePacket {
    pub quantum_context: GpuQuantumContext,
    pub particle_params: ParticlePhysicsParams,
    pub bridge_digest: [u8; 32],
    pub seal_digest: [u8; 32],
    pub frame: u64,
    pub observer_coherence: f32,
    pub observer_decohere_radius: f32,
    // ── Metaphor blocks (Phase 4) ────────────────────────
    pub wave_params: WaveParams,
    pub matter: dreamwell_metaphors::MatterBlock,
    pub cull_tier: MaterializationTier,
    pub event_cues: Vec<EventCue>,
    pub material_roughness_bias: f32,
    pub material_emissive_bias: f32,
    pub material_aniso_bias: f32,
    pub schema_version: u32,
}

/// Validation result from bridge().
pub struct BridgeValidation {
    pub context_valid: bool,
    pub used_fallback: bool,
    pub error_message: Option<&'static str>,
}

// ── Bridge Metrics ────────────────────────────────────────────────────

/// Prometheus-compatible metrics for the bridge boundary.
pub struct BridgeMetrics {
    pub frames_bridged: u64,
    pub fallback_count: u64,
    pub last_bridge_ns: u64,
    pub memory_captured: u64,
    pub seal_chain_depth: u64,
}

impl Default for BridgeMetrics {
    fn default() -> Self {
        Self {
            frames_bridged: 0,
            fallback_count: 0,
            last_bridge_ns: 0,
            memory_captured: 0,
            seal_chain_depth: 0,
        }
    }
}

// ── QuantumBridge ─────────────────────────────────────────────────────

/// Formal CPU→GPU transfer with DreamMemory capture and SacredSeal provenance.
pub struct QuantumBridge {
    particle_count: u32,
    memory: DreamMemory,
    metrics: BridgeMetrics,
    last_seal: [u8; 32],
    seal_depth: u64,
    epoch: std::time::Instant,
    /// MetaphorEvaluator — produces full metaphor blocks from attention + tags.
    evaluator: MetaphorEvaluator,
    /// Per-tick tag influence frame (set by caller before bridge()).
    tag_frame: TagInfluenceFrame,
    /// Previous frame's anchor position for GPU velocity derivation.
    prev_anchor: [f32; 3],
}

impl QuantumBridge {
    pub fn new(particle_count: u32) -> Self {
        Self::with_memory_capacity(particle_count, 120)
    }

    /// Create a QuantumBridge with a custom DreamMemory ring capacity.
    pub fn with_memory_capacity(particle_count: u32, memory_capacity: usize) -> Self {
        Self {
            particle_count,
            memory: DreamMemory::new(memory_capacity),
            metrics: BridgeMetrics::default(),
            last_seal: [0u8; 32],
            seal_depth: 0,
            epoch: std::time::Instant::now(),
            evaluator: MetaphorEvaluator::default(),
            tag_frame: TagInfluenceFrame::empty(),
            prev_anchor: [0.0; 3],
        }
    }

    /// Replace the active metaphor profile.
    pub fn set_metaphor_profile(&mut self, profile: MetaphorProfile) {
        self.evaluator.set_profile(profile);
    }

    /// Set the per-tick tag influence frame.
    /// Call before bridge() to provide PropertyTag context.
    pub fn set_tag_frame(&mut self, tags: TagInfluenceFrame) {
        self.tag_frame = tags;
    }

    /// Access the evaluator (for profile inspection).
    pub fn evaluator(&self) -> &MetaphorEvaluator {
        &self.evaluator
    }

    /// Bridge an EncodeResult to GPU-ready packets.
    /// Validates, captures to DreamMemory, chain-links seal.
    pub fn bridge(&mut self, encode: &EncodeResult, dt: f32, time_seconds: f32) -> (BridgePacket, BridgeValidation) {
        let start = std::time::Instant::now();
        let frame = encode.descriptor.frame_index;

        // 1. Map observables → GpuQuantumContext
        let obs = &encode.observables;
        let mut gpu_ctx = GpuQuantumContext {
            populations: obs.populations,
            coherence_magnitude: obs.coherence_magnitude,
            purity: obs.purity,
            entropy: obs.entropy,
            coherences: obs.coherences,
            decoherence_epsilon: obs.decoherence_epsilon,
            temperature: obs.temperature,
            phase_time: obs.phase_time,
            measured_mode: obs.measured_mode,
            feature_flags: encode.descriptor.feature_flags.bits(),
            free_energy: obs.free_energy,
            asymmetry: obs.asymmetry,
            _pad_ff_c: 0,
        };

        // 2. Validate — fallback to classical on failure
        let validation = match gpu_ctx.validate() {
            Ok(()) => BridgeValidation {
                context_valid: true,
                used_fallback: false,
                error_message: None,
            },
            Err(msg) => {
                gpu_ctx = GpuQuantumContext::classical();
                self.metrics.fallback_count += 1;
                BridgeValidation {
                    context_valid: false,
                    used_fallback: true,
                    error_message: Some(msg),
                }
            }
        };

        // 3. Evaluate metaphor from descriptor + tags
        self.evaluator.advance_time(dt);
        let mut sorted_tags = self.tag_frame.clone();
        sorted_tags.sort_deterministic();
        let metaphor = self
            .evaluator
            .evaluate(&encode.descriptor, &sorted_tags, Some(&encode.wave_intent));

        // 4. Build ParticlePhysicsParams from metaphor output (prev_anchor for GPU velocity)
        let particle_params = build_particle_physics_params_from_metaphor(
            &encode.descriptor,
            &metaphor,
            dt,
            self.particle_count,
            time_seconds,
            self.prev_anchor,
        );
        self.prev_anchor = encode.descriptor.position;

        // 4. Compute bridge_digest = BLAKE3(bytes_of(gpu_ctx))
        let bridge_digest = *blake3::hash(bytemuck::bytes_of(&gpu_ctx)).as_bytes();

        // 5. Compute seal_digest = BLAKE3(last_seal ‖ bridge_digest ‖ encode_digest)
        let seal_digest = {
            let mut hasher = blake3::Hasher::new();
            hasher.update(&self.last_seal);
            hasher.update(&bridge_digest);
            hasher.update(&encode.encode_digest);
            *hasher.finalize().as_bytes()
        };
        self.last_seal = seal_digest;
        self.seal_depth += 1;

        // 6. Observer coherence from attention state
        let observer_coherence = encode.descriptor.attention.coherence;
        let observer_decohere_radius = encode.descriptor.attention.decoherence.max(0.0) * 100.0 + 200.0;

        // 7. Capture to DreamMemory
        let timestamp_ns = self.epoch.elapsed().as_nanos() as u64;
        self.memory.capture(DreamMemoryEntry {
            frame,
            quantum_context: gpu_ctx,
            particle_params,
            encode_digest: encode.encode_digest,
            bridge_digest,
            seal_digest,
            coherence: observer_coherence,
            feature_flags: gpu_ctx.feature_flags,
            timestamp_ns,
            validation_passed: validation.context_valid,
        });

        // 8. Update metrics
        let elapsed_ns = start.elapsed().as_nanos() as u64;
        self.metrics.frames_bridged += 1;
        self.metrics.last_bridge_ns = elapsed_ns;
        self.metrics.memory_captured = self.memory.total_captured();
        self.metrics.seal_chain_depth = self.seal_depth;

        let packet = BridgePacket {
            quantum_context: gpu_ctx,
            particle_params,
            bridge_digest,
            seal_digest,
            frame,
            observer_coherence,
            observer_decohere_radius,
            // Metaphor blocks
            wave_params: metaphor.wave_params,
            matter: metaphor.matter,
            cull_tier: metaphor.cull.self_tier,
            event_cues: metaphor.events.active_cues,
            material_roughness_bias: metaphor.material.roughness_bias,
            material_emissive_bias: metaphor.material.emissive_bias,
            material_aniso_bias: metaphor.material.aniso_bias,
            schema_version: BRIDGE_SCHEMA_VERSION,
        };

        (packet, validation)
    }

    /// Current bridge metrics snapshot.
    pub fn bridge_metrics(&self) -> &BridgeMetrics {
        &self.metrics
    }

    /// Access the DreamMemory ring buffer.
    pub fn memory(&self) -> &DreamMemory {
        &self.memory
    }

    /// Current seal chain depth.
    pub fn seal_depth(&self) -> u64 {
        self.seal_depth
    }
}

// ── Unit Tests ────────────────────────────────────────────────────────

#[cfg(test)]
mod tests {
    use super::*;
    use dreamwell_attention::encoder::CausalEngineEncoder;
    use dreamwell_attention::{
        AttentionState, CausalComputeKernel, InputPacket, LocomotionShape, SuperBodyController, SuperBodyFeatureFlags,
        SuperBodyFrameDescriptor,
    };

    fn idle_input() -> InputPacket {
        InputPacket::idle(1.0 / 60.0, 0)
    }

    fn make_encode_result() -> EncodeResult {
        let mut enc = CausalEngineEncoder::new_instant([0.0, 0.5, 0.0], 128, 42);
        enc.encode_frame(&idle_input(), &[])
    }

    #[test]
    fn bridge_produces_valid_context() {
        let mut bridge = QuantumBridge::new(128);
        let encode = make_encode_result();
        let (packet, validation) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        assert!(validation.context_valid);
        assert!(!validation.used_fallback);
        // Populations should sum to ~1.0
        let sum: f32 = packet.quantum_context.populations.iter().sum();
        assert!((sum - 1.0).abs() < 0.01, "populations sum = {sum}");
    }

    #[test]
    fn bridge_populations_match_observables() {
        let mut bridge = QuantumBridge::new(128);
        let encode = make_encode_result();
        let (packet, _) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        for i in 0..5 {
            assert!((packet.quantum_context.populations[i] - encode.observables.populations[i]).abs() < 0.001);
        }
    }

    #[test]
    fn bridge_fallback_on_nan() {
        let mut bridge = QuantumBridge::new(128);
        let mut encode = make_encode_result();
        // Corrupt observables with NaN
        encode.observables.populations[0] = f32::NAN;
        let (_, validation) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        assert!(validation.used_fallback);
        assert!(validation.error_message.is_some());
    }

    #[test]
    fn bridge_fallback_on_bad_trace() {
        let mut bridge = QuantumBridge::new(128);
        let mut encode = make_encode_result();
        // Corrupt: populations don't sum to 1
        encode.observables.populations = [0.5, 0.5, 0.5, 0.5, 0.5]; // sum = 2.5
        let (_, validation) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        assert!(validation.used_fallback);
    }

    #[test]
    fn bridge_digest_nonzero() {
        let mut bridge = QuantumBridge::new(128);
        let encode = make_encode_result();
        let (packet, _) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        assert_ne!(packet.bridge_digest, [0u8; 32]);
    }

    #[test]
    fn bridge_seal_chain_links() {
        let mut bridge = QuantumBridge::new(128);
        let mut enc = CausalEngineEncoder::new_instant([0.0, 0.5, 0.0], 128, 42);

        let e1 = enc.encode_frame(&idle_input(), &[]);
        let (p1, _) = bridge.bridge(&e1, 1.0 / 60.0, 0.0);

        let e2 = enc.encode_frame(&idle_input(), &[]);
        let (p2, _) = bridge.bridge(&e2, 1.0 / 60.0, 1.0 / 60.0);

        // Each seal should be unique and non-zero.
        assert_ne!(p1.seal_digest, [0u8; 32]);
        assert_ne!(p2.seal_digest, [0u8; 32]);
        assert_ne!(p1.seal_digest, p2.seal_digest, "seals should differ per frame");
        assert_eq!(bridge.seal_depth(), 2);
    }

    #[test]
    fn bridge_dream_memory_captures() {
        let mut bridge = QuantumBridge::new(120);
        let mut enc = CausalEngineEncoder::new_instant([0.0, 0.5, 0.0], 128, 42);

        for i in 0..5 {
            let mut input = idle_input();
            input.timestamp = i as f64 / 60.0;
            let encode = enc.encode_frame(&input, &[]);
            let _ = bridge.bridge(&encode, 1.0 / 60.0, i as f32 / 60.0);
        }

        assert_eq!(bridge.memory().total_captured(), 5);
        let recent = bridge.memory().recent(3);
        assert_eq!(recent.len(), 3);
    }

    #[test]
    fn bridge_dream_memory_ring_wraps() {
        let mut bridge = QuantumBridge::with_memory_capacity(128, 4); // tiny ring
        let mut enc = CausalEngineEncoder::new_instant([0.0, 0.5, 0.0], 128, 42);

        for i in 0..10 {
            let mut input = idle_input();
            input.timestamp = i as f64 / 60.0;
            let encode = enc.encode_frame(&input, &[]);
            let _ = bridge.bridge(&encode, 1.0 / 60.0, i as f32 / 60.0);
        }

        assert_eq!(bridge.memory().total_captured(), 10);
        // Ring capacity 4: only last 4 entries accessible.
        let recent = bridge.memory().recent(10);
        assert_eq!(recent.len(), 4);
    }

    #[test]
    fn bridge_packet_has_wave_params() {
        let mut bridge = QuantumBridge::new(128);
        let encode = make_encode_result();
        let (packet, _) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        // Idle encoder → decoherence should be high, shell_bias > 0
        assert!(packet.wave_params.decoherence >= 0.0);
        assert!(packet.wave_params.coherence >= 0.0);
        assert_eq!(packet.schema_version, BRIDGE_SCHEMA_VERSION);
    }

    #[test]
    fn bridge_packet_cull_tier_is_valid() {
        let mut bridge = QuantumBridge::new(128);
        let encode = make_encode_result();
        let (packet, _) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        assert!(packet.cull_tier as u8 <= 3);
    }

    #[test]
    fn bridge_with_custom_profile() {
        let mut bridge = QuantumBridge::new(128);
        bridge.set_metaphor_profile(dreamwell_metaphors::MetaphorProfile::keynote_fibonacci());
        let encode = make_encode_result();
        let (packet, validation) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        assert!(validation.context_valid);
        // Fibonacci profile has lower spring_k
        assert!(packet.particle_params.spring_k < 4.0);
    }

    #[test]
    fn bridge_with_tag_frame() {
        let mut bridge = QuantumBridge::new(128);
        let mut tags = dreamwell_metaphors::TagInfluenceFrame::empty();
        tags.poi_couplings.push(dreamwell_metaphors::PoiCoupling {
            poi_id: 42,
            distance: 3.0,
            coupling: 0.8,
            siphon_eligible: true,
        });
        bridge.set_tag_frame(tags);
        let encode = make_encode_result();
        let (packet, _) = bridge.bridge(&encode, 1.0 / 60.0, 0.0);
        assert!(packet.wave_params.siphon_bias > 0.0);
    }
}
