#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-qcg-wasm -- Quantum Code Gen WASM Runtime
//! JIT-compile & execute WASM bytecode with sandboxed memory

use std::collections::HashMap;

#[derive(Debug)]
pub enum WasmError { CompileFailed(String), RuntimeError(String), MemoryError(String), LinkError(String) }
impl std::fmt::Display for WasmError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self { Self::CompileFailed(e) => write!(f, "Compile: {e}"), Self::RuntimeError(e) => write!(f, "Runtime: {e}"), Self::MemoryError(e) => write!(f, "Memory: {e}"), Self::LinkError(e) => write!(f, "Link: {e}") }
    }
}
impl std::error::Error for WasmError {}

#[derive(Debug, Clone)]
pub enum WasmValue { I32(i32), I64(i64), F32(f32), F64(f64) }

#[derive(Debug, Clone)]
pub struct WasmModule {
    pub name: String,
    pub exports: Vec<WasmExport>,
    pub memory_pages: u32,
    pub bytecode_size: usize,
}

#[derive(Debug, Clone)]
pub struct WasmExport { pub name: String, pub kind: ExportKind, pub params: Vec<String>, pub returns: Vec<String> }

#[derive(Debug, Clone)]
pub enum ExportKind { Function, Memory, Table, Global }

/// Sandboxed WASM memory (page-based, 64KB per page)
pub struct WasmMemory {
    pages: Vec<Vec<u8>>,
    max_pages: u32,
}

impl WasmMemory {
    pub fn new(initial_pages: u32, max_pages: u32) -> Result<Self, WasmError> {
        if initial_pages > max_pages { return Err(WasmError::MemoryError(format!("{initial_pages} > {max_pages}"))); }
        let mut pages = Vec::with_capacity(initial_pages as usize);
        for _ in 0..initial_pages { pages.push(vec![0u8; 65536]); }
        Ok(Self { pages, max_pages })
    }

    pub fn grow(&mut self, delta: u32) -> Result<u32, WasmError> {
        let old = self.pages.len() as u32;
        if old + delta > self.max_pages { return Err(WasmError::MemoryError(format!("Cannot grow beyond {}", self.max_pages))); }
        for _ in 0..delta { self.pages.push(vec![0u8; 65536]); }
        Ok(old)
    }

    pub fn read(&self, offset: usize, len: usize) -> Result<Vec<u8>, WasmError> {
        let total = self.pages.len() * 65536;
        if offset + len > total { return Err(WasmError::MemoryError(format!("OOB: {offset}+{len} > {total}"))); }
        let mut result = Vec::with_capacity(len);
        for i in offset..offset + len {
            let page = i / 65536;
            let off = i % 65536;
            result.push(self.pages[page][off]);
        }
        Ok(result)
    }

    pub fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), WasmError> {
        let total = self.pages.len() * 65536;
        if offset + data.len() > total { return Err(WasmError::MemoryError(format!("OOB write"))); }
        for (i, &byte) in data.iter().enumerate() {
            let addr = offset + i;
            let page = addr / 65536;
            let off = addr % 65536;
            self.pages[page][off] = byte;
        }
        Ok(())
    }

    pub fn size_bytes(&self) -> usize { self.pages.len() * 65536 }
    pub fn page_count(&self) -> u32 { self.pages.len() as u32 }
}

/// WASM Instance runtime
pub struct WasmInstance {
    module: WasmModule,
    memory: WasmMemory,
    globals: HashMap<String, WasmValue>,
    call_count: u64,
}

impl WasmInstance {
    pub fn new(module: WasmModule, max_memory_pages: u32) -> Result<Self, WasmError> {
        let memory = WasmMemory::new(module.memory_pages, max_memory_pages)?;
        Ok(Self { module, memory, globals: HashMap::new(), call_count: 0 })
    }

    pub fn set_global(&mut self, name: &str, val: WasmValue) { self.globals.insert(name.into(), val); }
    pub fn get_global(&self, name: &str) -> Option<&WasmValue> { self.globals.get(name) }

    /// Call exported function (simplified interpreter)
    pub fn call(&mut self, name: &str, args: &[WasmValue]) -> Result<Vec<WasmValue>, WasmError> {
        let export = self.module.exports.iter().find(|e| e.name == name && matches!(e.kind, ExportKind::Function))
            .ok_or_else(|| WasmError::LinkError(format!("Export '{name}' not found")))?;
        if args.len() != export.params.len() {
            return Err(WasmError::RuntimeError(format!("Expected {} args, got {}", export.params.len(), args.len())));
        }
        self.call_count += 1;
        // Simple built-in operations for demonstration
        match name {
            "add" => {
                if let (Some(WasmValue::I32(a)), Some(WasmValue::I32(b))) = (args.get(0), args.get(1)) {
                    Ok(vec![WasmValue::I32(a.wrapping_add(*b))])
                } else { Err(WasmError::RuntimeError("Type mismatch".into())) }
            }
            "mul" => {
                if let (Some(WasmValue::I64(a)), Some(WasmValue::I64(b))) = (args.get(0), args.get(1)) {
                    Ok(vec![WasmValue::I64(a.wrapping_mul(*b))])
                } else { Err(WasmError::RuntimeError("Type mismatch".into())) }
            }
            _ => Ok(vec![]) // Unknown export returns empty
        }
    }

    pub fn memory(&self) -> &WasmMemory { &self.memory }
    pub fn memory_mut(&mut self) -> &mut WasmMemory { &mut self.memory }
    pub fn call_count(&self) -> u64 { self.call_count }
}

#[cfg(test)]
mod tests {
    use super::*;
    fn test_module() -> WasmModule {
        WasmModule { name: "test".into(), memory_pages: 1, bytecode_size: 100,
            exports: vec![WasmExport { name: "add".into(), kind: ExportKind::Function, params: vec!["i32".into(), "i32".into()], returns: vec!["i32".into()] }] }
    }
    #[test]
    fn test_memory_rw() {
        let mut mem = WasmMemory::new(1, 4).unwrap();
        mem.write(100, &[1, 2, 3, 4]).unwrap();
        let data = mem.read(100, 4).unwrap();
        assert_eq!(data, vec![1, 2, 3, 4]);
    }
    #[test]
    fn test_memory_grow() {
        let mut mem = WasmMemory::new(1, 4).unwrap();
        assert_eq!(mem.page_count(), 1);
        mem.grow(2).unwrap();
        assert_eq!(mem.page_count(), 3);
        assert!(mem.grow(5).is_err());
    }
    #[test]
    fn test_call() {
        let mut inst = WasmInstance::new(test_module(), 4).unwrap();
        let result = inst.call("add", &[WasmValue::I32(10), WasmValue::I32(20)]).unwrap();
        if let Some(WasmValue::I32(n)) = result.first() { assert_eq!(*n, 30); }
    }
    #[test]
    fn test_oob() {
        let mem = WasmMemory::new(1, 1).unwrap();
        assert!(mem.read(70000, 1).is_err());
    }
}
