//! WebAssembly REPL implementation for browser-based evaluation
//!
//! Provides interactive Ruchy evaluation in the browser with progressive enhancement.

use std::collections::HashMap;

#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

#[cfg(not(target_arch = "wasm32"))]
use serde::{Serialize, Deserialize};

// For non-WASM builds, provide stub types
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, Clone)]
pub struct JsValue;

// ============================================================================
// REPL Output Types
// ============================================================================

#[derive(Debug, Clone)]
#[cfg_attr(not(target_arch = "wasm32"), derive(Serialize, Deserialize))]
pub struct ReplOutput {
    pub success: bool,
    pub display: Option<String>,
    pub type_info: Option<String>,
    pub rust_code: Option<String>,
    pub error: Option<String>,
    pub timing: TimingInfo,
}

#[derive(Debug, Clone)]
#[cfg_attr(not(target_arch = "wasm32"), derive(Serialize, Deserialize))]
pub struct TimingInfo {
    pub parse_ms: f64,
    pub typecheck_ms: f64,
    pub eval_ms: f64,
    pub total_ms: f64,
}

// ============================================================================
// WASM REPL Implementation
// ============================================================================

#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub struct WasmRepl {
    /// Bindings for variables
    bindings: HashMap<String, String>,
    /// Command history
    history: Vec<String>,
    /// Session ID for tracking
    session_id: String,
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
impl WasmRepl {
    /// Create a new WASM REPL instance
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
    pub fn new() -> Result<WasmRepl, JsValue> {
        #[cfg(target_arch = "wasm32")]
        console_error_panic_hook::set_once();
        
        Ok(WasmRepl {
            bindings: HashMap::new(),
            history: Vec::new(),
            session_id: generate_session_id(),
        })
    }
    
    /// Evaluate a Ruchy expression
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    pub fn eval(&mut self, input: &str) -> Result<String, JsValue> {
        let start = get_timestamp();
        
        // Parse the input
        let parse_start = get_timestamp();
        let mut parser = crate::Parser::new(input);
        let ast = match parser.parse() {
            Ok(ast) => ast,
            Err(e) => {
                return Ok(serde_json::to_string(&ReplOutput {
                    success: false,
                    display: None,
                    type_info: None,
                    rust_code: None,
                    error: Some(format!("Parse error: {e}")),
                    timing: TimingInfo {
                        parse_ms: get_timestamp() - parse_start,
                        typecheck_ms: 0.0,
                        eval_ms: 0.0,
                        total_ms: get_timestamp() - start,
                    },
                }).unwrap_or_else(|_| "Error serializing output".to_string()));
            }
        };
        let parse_time = get_timestamp() - parse_start;
        
        // Type checking would go here
        let typecheck_start = get_timestamp();
        // ... type checking implementation ...
        let typecheck_time = get_timestamp() - typecheck_start;
        
        // Evaluation
        let eval_start = get_timestamp();
        // For now, just return the parsed AST as a string
        let result = format!("{ast:?}");
        let eval_time = get_timestamp() - eval_start;
        
        // Add to history
        self.history.push(input.to_string());
        
        // Return result
        Ok(serde_json::to_string(&ReplOutput {
            success: true,
            display: Some(result),
            type_info: Some("Any".to_string()),
            rust_code: None,
            error: None,
            timing: TimingInfo {
                parse_ms: parse_time,
                typecheck_ms: typecheck_time,
                eval_ms: eval_time,
                total_ms: get_timestamp() - start,
            },
        }).unwrap_or_else(|_| "Error serializing output".to_string()))
    }
    
    /// Get command history
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    pub fn get_history(&self) -> Vec<String> {
        self.history.clone()
    }
    
    /// Clear the REPL state
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    pub fn clear(&mut self) {
        self.bindings.clear();
        self.history.clear();
    }
    
    /// Get session ID
    #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
    pub fn session_id(&self) -> String {
        self.session_id.clone()
    }
}

impl Default for WasmRepl {
    fn default() -> Self {
        Self::new().unwrap()
    }
}

// ============================================================================
// Helper Functions
// ============================================================================

/// Generate a unique session ID
fn generate_session_id() -> String {
    #[cfg(target_arch = "wasm32")]
    {
        // Use browser's crypto API for UUID
        format!("session-{}", js_sys::Date::now())
    }
    #[cfg(not(target_arch = "wasm32"))]
    {
        // Use system time for non-WASM builds
        format!("session-{}", std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_millis())
    }
}

/// Get current timestamp in milliseconds
fn get_timestamp() -> f64 {
    #[cfg(target_arch = "wasm32")]
    {
        js_sys::Date::now()
    }
    #[cfg(not(target_arch = "wasm32"))]
    {
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_millis() as f64
    }
}

// ============================================================================
// Memory Management
// ============================================================================

/// Heap allocator for WASM
pub struct WasmHeap {
    /// Young generation for short-lived objects
    young: Vec<u8>,
    /// Old generation for long-lived objects
    old: Vec<u8>,
    /// GC roots
    roots: Vec<usize>,
}

impl WasmHeap {
    pub fn new() -> Self {
        Self {
            young: Vec::with_capacity(256 * 1024), // 256KB
            old: Vec::with_capacity(2 * 1024 * 1024), // 2MB
            roots: Vec::new(),
        }
    }
    
    /// Perform minor garbage collection
    pub fn minor_gc(&mut self) {
        self.young.clear();
    }
    
    /// Perform major garbage collection
    pub fn major_gc(&mut self) {
        // Mark phase
        let mut marked = vec![false; self.old.len()];
        for &root in &self.roots {
            if root < marked.len() {
                marked[root] = true;
            }
        }
        
        // Compact phase (simplified)
        let mut compacted = Vec::new();
        for (i, &is_marked) in marked.iter().enumerate() {
            if is_marked && i < self.old.len() {
                compacted.push(self.old[i]);
            }
        }
        self.old = compacted;
    }
}

impl Default for WasmHeap {
    fn default() -> Self {
        Self::new()
    }
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_wasm_repl_creation() {
        let repl = WasmRepl::new();
        assert!(repl.is_ok());
    }
    
    #[test]
    fn test_session_id() {
        let repl = WasmRepl::new().unwrap();
        assert!(repl.session_id().starts_with("session-"));
    }
    
    #[test]
    fn test_heap() {
        let mut heap = WasmHeap::new();
        heap.minor_gc();
        heap.major_gc();
        assert!(heap.young.is_empty());
    }
}