/*!
 * VM Target Generator - Generate VM function table definitions
 */

use crate::bid::{BidDefinition, BidInterface, BidMethod, BidResult};
use crate::bid::codegen::{CodeGenOptions, GeneratedFile};
use std::path::PathBuf;

pub struct VmGenerator;

impl VmGenerator {
    /// Generate VM function table definitions
    pub fn generate(bid: &BidDefinition, options: &CodeGenOptions) -> BidResult<Vec<GeneratedFile>> {
        let bid_name = bid.name();
        let mut files = Vec::new();
        
        // Generate Rust function table definition
        let rust_content = Self::generate_rust_function_table(bid)?;
        let rust_path = options.output_dir.join(format!("{}_vm_table.rs", bid_name));
        files.push(GeneratedFile::new(rust_path, rust_content));
        
        // Generate dispatcher implementation
        let dispatcher_content = Self::generate_dispatcher(bid)?;
        let dispatcher_path = options.output_dir.join(format!("{}_vm_dispatcher.rs", bid_name));
        files.push(GeneratedFile::new(dispatcher_path, dispatcher_content));
        
        // Generate README
        let readme_content = Self::generate_readme(bid)?;
        let readme_path = options.output_dir.join("README.md");
        files.push(GeneratedFile::new(readme_path, readme_content));
        
        Ok(files)
    }
    
    /// Generate Rust function table definition
    fn generate_rust_function_table(bid: &BidDefinition) -> BidResult<String> {
        let bid_name = bid.name();
        let mut content = String::new();
        
        content.push_str(&format!(
            "// VM function table for {}\n",
            bid_name
        ));
        content.push_str("// Generated by Nyash BID code generator\n\n");
        
        content.push_str("use crate::bid::{{BidHandle, BidResult, BidRuntimeError}};\n");
        content.push_str("use crate::backend::vm::{{VM, ValueId}};\n");
        content.push_str("use std::collections::HashMap;\n\n");
        
        // Generate function type definitions
        content.push_str(&format!("/// Function table for {} interface\n", bid_name));
        content.push_str(&format!("pub struct {}VmTable {{\n", Self::pascal_case(&bid_name)));
        content.push_str("    functions: HashMap<String, VmFunction>,\n");
        content.push_str("}\n\n");
        
        content.push_str("/// VM function pointer type\n");
        content.push_str("type VmFunction = fn(&mut VM, &[ValueId]) -> BidResult<Option<ValueId>>;\n\n");
        
        content.push_str(&format!("impl {}VmTable {{\n", Self::pascal_case(&bid_name)));
        content.push_str("    /// Create new function table\n");
        content.push_str("    pub fn new() -> Self {\n");
        content.push_str("        let mut functions = HashMap::new();\n\n");
        
        // Add function mappings
        for interface in &bid.interfaces {
            for method in &interface.methods {
                let function_name = format!("{}.{}", interface.name, method.name);
                let impl_name = format!("impl_{}", method.name.replace("-", "_"));
                content.push_str(&format!(
                    "        functions.insert(\"{}\".to_string(), Self::{});\n",
                    function_name, impl_name
                ));
            }
        }
        
        content.push_str("\n        Self { functions }\n");
        content.push_str("    }\n\n");
        
        content.push_str("    /// Call a function by name\n");
        content.push_str("    pub fn call(&self, name: &str, vm: &mut VM, args: &[ValueId]) -> BidResult<Option<ValueId>> {\n");
        content.push_str("        if let Some(func) = self.functions.get(name) {\n");
        content.push_str("            func(vm, args)\n");
        content.push_str("        } else {\n");
        content.push_str("            Err(crate::bid::BidError::invalid_method())\n");
        content.push_str("        }\n");
        content.push_str("    }\n\n");
        
        // Generate function implementations
        for interface in &bid.interfaces {
            content.push_str(&format!("    // {} methods\n", interface.name));
            
            for method in &interface.methods {
                let impl_function = Self::generate_vm_function_impl(interface, method)?;
                content.push_str(&impl_function);
            }
            
            content.push_str("\n");
        }
        
        content.push_str("}\n\n");
        
        // Generate integration helper
        content.push_str("/// Integration with VM\n");
        content.push_str("pub fn register_vm_functions(vm: &mut VM) {\n");
        content.push_str(&format!("    let table = {}VmTable::new();\n", Self::pascal_case(&bid_name)));
        content.push_str("    \n");
        content.push_str("    // Register each function with the VM\n");
        content.push_str("    // TODO: Integrate with VM's external function registry\n");
        content.push_str("    // vm.register_external_function_table(table);\n");
        content.push_str("}\n");
        
        Ok(content)
    }
    
    /// Generate VM function implementation
    fn generate_vm_function_impl(interface: &BidInterface, method: &BidMethod) -> BidResult<String> {
        let impl_name = format!("impl_{}", method.name.replace("-", "_"));
        let mut content = String::new();
        
        content.push_str(&format!("    /// Implementation of {}.{}\n", interface.name, method.name));
        content.push_str(&format!("    fn {}(vm: &mut VM, args: &[ValueId]) -> BidResult<Option<ValueId>> {{\n", impl_name));
        
        // Parameter validation
        content.push_str(&format!("        // Validate argument count\n"));
        content.push_str(&format!("        if args.len() != {} {{\n", method.params.len()));
        content.push_str("            return Err(crate::bid::BidError::invalid_args());\n");
        content.push_str("        }\n\n");
        
        // Extract parameters
        for (i, param) in method.params.iter().enumerate() {
            let param_name = param.get_name().unwrap_or_else(|| format!("param_{}", i));
            content.push_str(&format!("        let {} = &args[{}]; // {}\n", 
                                    param_name, i, param.get_type()));
        }
        
        content.push_str("\n        // TODO: Implement actual method logic\n");
        content.push_str(&format!("        println!(\"VM: Called {}.{}({{:?}})\", args);\n", 
                                interface.name, method.name));
        
        // Return value
        if method.returns.is_void() {
            content.push_str("\n        Ok(None) // void return\n");
        } else {
            content.push_str(&format!("\n        // TODO: Return proper {} value\n", method.returns.type_name()));
            content.push_str("        let result_id = vm.allocate_value(crate::value::NyashValue::Integer(0));\n");
            content.push_str("        Ok(Some(result_id))\n");
        }
        
        content.push_str("    }\n\n");
        
        Ok(content)
    }
    
    /// Generate dispatcher implementation
    fn generate_dispatcher(bid: &BidDefinition) -> BidResult<String> {
        let bid_name = bid.name();
        let mut content = String::new();
        
        content.push_str(&format!(
            "// VM dispatcher for {}\n",
            bid_name
        ));
        content.push_str("// Generated by Nyash BID code generator\n\n");
        
        content.push_str("use crate::backend::vm::VM;\n");
        content.push_str("use crate::mir::MirInstruction;\n");
        content.push_str("use crate::bid::BidResult;\n\n");
        
        content.push_str(&format!("/// Dispatcher for {} VM calls\n", bid_name));
        content.push_str(&format!("pub struct {}VmDispatcher;\n\n", Self::pascal_case(&bid_name)));
        
        content.push_str(&format!("impl {}VmDispatcher {{\n", Self::pascal_case(&bid_name)));
        content.push_str("    /// Handle ExternCall MIR instruction\n");
        content.push_str("    pub fn handle_extern_call(\n");
        content.push_str("        vm: &mut VM,\n");
        content.push_str("        interface: &str,\n");
        content.push_str("        method: &str,\n");
        content.push_str("        args: &[crate::backend::vm::ValueId],\n");
        content.push_str("        dst: Option<crate::backend::vm::ValueId>\n");
        content.push_str("    ) -> BidResult<()> {\n");
        
        content.push_str("        let function_name = format!(\"{}.{}\", interface, method);\n");
        content.push_str("        \n");
        content.push_str("        match function_name.as_str() {\n");
        
        // Generate match arms for each method
        for interface in &bid.interfaces {
            for method in &interface.methods {
                let function_name = format!("{}.{}", interface.name, method.name);
                content.push_str(&format!("            \"{}\" => {{\n", function_name));
                content.push_str(&format!("                // TODO: Call actual {} implementation\n", method.name));
                content.push_str("                println!(\"VM Dispatcher: {} called\");\n");
                
                if !method.returns.is_void() {
                    content.push_str("                if let Some(dst_id) = dst {\n");
                    content.push_str("                    // Set return value\n");
                    content.push_str("                    vm.set_value(dst_id, crate::value::NyashValue::Integer(0));\n");
                    content.push_str("                }\n");
                }
                
                content.push_str("                Ok(())\n");
                content.push_str("            },\n");
            }
        }
        
        content.push_str("            _ => {\n");
        content.push_str("                Err(crate::bid::BidError::invalid_method())\n");
        content.push_str("            }\n");
        content.push_str("        }\n");
        content.push_str("    }\n");
        content.push_str("}\n");
        
        Ok(content)
    }
    
    /// Generate README
    fn generate_readme(bid: &BidDefinition) -> BidResult<String> {
        let bid_name = bid.name();
        let mut content = String::new();
        
        content.push_str(&format!("# {} - VM Integration\n\n", bid_name));
        content.push_str("Generated by Nyash BID code generator for VM target.\n\n");
        
        content.push_str("## Files\n\n");
        content.push_str(&format!("- `{}_vm_table.rs` - VM function table definitions\n", bid_name));
        content.push_str(&format!("- `{}_vm_dispatcher.rs` - VM dispatcher implementation\n", bid_name));
        content.push_str("- `README.md` - This file\n\n");
        
        content.push_str("## Integration\n\n");
        content.push_str("1. Add the generated files to your VM backend:\n");
        content.push_str("   ```rust\n");
        content.push_str(&format!("   mod {}_vm_table;\n", bid_name));
        content.push_str(&format!("   mod {}_vm_dispatcher;\n", bid_name));
        content.push_str("   ```\n\n");
        
        content.push_str("2. Register the function table:\n");
        content.push_str("   ```rust\n");
        content.push_str(&format!("   {}::register_vm_functions(&mut vm);\n", bid_name));
        content.push_str("   ```\n\n");
        
        content.push_str("3. Handle ExternCall instructions:\n");
        content.push_str("   ```rust\n");
        content.push_str("   match instruction {\n");
        content.push_str("       MirInstruction::ExternCall { interface, method, args, dst } => {\n");
        content.push_str(&format!("           {}VmDispatcher::handle_extern_call(\n", Self::pascal_case(&bid_name)));
        content.push_str("               &mut vm, interface, method, args, *dst\n");
        content.push_str("           )?\n");
        content.push_str("       }\n");
        content.push_str("       // ... other instructions\n");
        content.push_str("   }\n");
        content.push_str("   ```\n\n");
        
        content.push_str("## Available Functions\n\n");
        for interface in &bid.interfaces {
            content.push_str(&format!("### {}\n\n", interface.name));
            for method in &interface.methods {
                content.push_str(&format!("- `{}(", method.name));
                let param_strs: Vec<String> = method.params.iter().map(|p| {
                    format!("{}: {}", 
                            p.get_name().unwrap_or_else(|| "param".to_string()),
                            p.get_type())
                }).collect();
                content.push_str(&param_strs.join(", "));
                content.push_str(&format!(")` → `{}`\n", method.returns.type_name()));
            }
            content.push_str("\n");
        }
        
        Ok(content)
    }
    
    /// Convert snake_case to PascalCase
    fn pascal_case(s: &str) -> String {
        s.split('_')
            .map(|word| {
                let mut chars = word.chars();
                match chars.next() {
                    None => String::new(),
                    Some(first) => first.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase(),
                }
            })
            .collect()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_pascal_case() {
        assert_eq!(VmGenerator::pascal_case("console"), "Console");
        assert_eq!(VmGenerator::pascal_case("file_box"), "FileBox");
        assert_eq!(VmGenerator::pascal_case("my_long_name"), "MyLongName");
    }
}