//! Interactive REPL for JVM exploration
//!
//! Provides a command-line interface for interacting with JVMRS,
//! exploring classes, methods, fields, and executing code.

use crate::error::{JvmError, RuntimeError};
use crate::interpreter::Interpreter;
use std::io::{self, Write};

/// REPL command types
#[derive(Debug, Clone, PartialEq)]
pub enum ReplCommand {
    /// Load a class
    LoadClass(String),

    /// Show class information
    ShowClass(String),

    /// Show method information
    ShowMethod(String, String),

    /// Show field information
    ShowField(String, String),

    /// Execute a method
    Execute(String, String, Vec<String>),

    /// Create an instance
    NewInstance(String, Vec<String>),

    /// Get a field value
    GetField(String, String, String),

    /// Set a field value
    SetField(String, String, String, String),

    /// Show current state
    ShowState,

    /// Show help
    Help,

    /// Exit REPL
    Exit,

    /// Unknown command
    Unknown(String),
}

/// Interactive REPL for JVM exploration
pub struct JvmRepl {
    interpreter: Interpreter,
    running: bool,
    /// Named object registry: user-visible name -> heap address
    named_objects: std::collections::HashMap<String, u32>,
}

impl JvmRepl {
    /// Create a new REPL instance
    pub fn new() -> Self {
        Self {
            interpreter: Interpreter::new(),
            running: true,
            named_objects: std::collections::HashMap::new(),
        }
    }

    /// Run the REPL loop
    pub fn run(&mut self) {
        println!("JVMRS Interactive REPL");
        println!("Type 'help' for available commands\n");

        while self.running {
            print!("jvmrs> ");
            if let Err(e) = io::stdout().flush() {
                eprintln!("Error flushing stdout: {}", e);
            }

            let mut input = String::new();
            match io::stdin().read_line(&mut input) {
                Ok(0) => break, // EOF
                Ok(_) => {
                    let command = self.parse_command(&input.trim());
                    self.execute_command(command);
                }
                Err(e) => {
                    eprintln!("Error reading input: {}", e);
                }
            }
        }
    }

    /// Parse a command from input string
    fn parse_command(&self, input: &str) -> ReplCommand {
        let parts: Vec<&str> = input.split_whitespace().collect();

        if parts.is_empty() {
            return ReplCommand::Unknown(String::new());
        }

        match parts[0].to_lowercase().as_str() {
            "load" => {
                if parts.len() >= 2 {
                    ReplCommand::LoadClass(parts[1].to_string())
                } else {
                    ReplCommand::Unknown(input.to_string())
                }
            }
            "class" => {
                if parts.len() >= 2 {
                    ReplCommand::ShowClass(parts[1].to_string())
                } else {
                    ReplCommand::Unknown(input.to_string())
                }
            }
            "method" => {
                if parts.len() >= 3 {
                    ReplCommand::ShowMethod(parts[1].to_string(), parts[2].to_string())
                } else {
                    ReplCommand::Unknown(input.to_string())
                }
            }
            "field" => {
                if parts.len() >= 3 {
                    ReplCommand::ShowField(parts[1].to_string(), parts[2].to_string())
                } else {
                    ReplCommand::Unknown(input.to_string())
                }
            }
            "exec" => {
                if parts.len() >= 2 {
                    let class = parts[1].to_string();
                    let method = if parts.len() >= 3 {
                        parts[2].to_string()
                    } else {
                        "main".to_string()
                    };
                    let args = if parts.len() >= 4 {
                        parts[3..]
                            .to_vec()
                            .into_iter()
                            .map(|s| s.to_string())
                            .collect()
                    } else {
                        Vec::new()
                    };
                    ReplCommand::Execute(class, method, args)
                } else {
                    ReplCommand::Unknown(input.to_string())
                }
            }
            "new" => {
                if parts.len() >= 2 {
                    let class = parts[1].to_string();
                    let args = if parts.len() >= 3 {
                        parts[2..]
                            .to_vec()
                            .into_iter()
                            .map(|s| s.to_string())
                            .collect()
                    } else {
                        Vec::new()
                    };
                    ReplCommand::NewInstance(class, args)
                } else {
                    ReplCommand::Unknown(input.to_string())
                }
            }
            "get" => {
                if parts.len() >= 4 {
                    ReplCommand::GetField(
                        parts[1].to_string(),
                        parts[2].to_string(),
                        parts[3].to_string(),
                    )
                } else {
                    ReplCommand::Unknown(input.to_string())
                }
            }
            "set" => {
                if parts.len() >= 5 {
                    ReplCommand::SetField(
                        parts[1].to_string(),
                        parts[2].to_string(),
                        parts[3].to_string(),
                        parts[4].to_string(),
                    )
                } else {
                    ReplCommand::Unknown(input.to_string())
                }
            }
            "state" => ReplCommand::ShowState,
            "help" => ReplCommand::Help,
            "exit" | "quit" => ReplCommand::Exit,
            _ => ReplCommand::Unknown(input.to_string()),
        }
    }

    /// Execute a REPL command
    fn execute_command(&mut self, command: ReplCommand) {
        match command {
            ReplCommand::LoadClass(class_name) => match self.interpreter.load_class(&class_name) {
                Ok(_) => println!("Loaded class: {}", class_name),
                Err(e) => eprintln!("Error loading class: {:?}", e),
            },
            ReplCommand::ShowClass(class_name) => {
                self.show_class_info(&class_name);
            }
            ReplCommand::ShowMethod(class_name, method_name) => {
                self.show_method_info(&class_name, &method_name);
            }
            ReplCommand::ShowField(class_name, field_name) => {
                self.show_field_info(&class_name, &field_name);
            }
            ReplCommand::Execute(class_name, method_name, args) => {
                self.execute_method(&class_name, &method_name, args);
            }
            ReplCommand::NewInstance(class_name, args) => {
                self.new_instance(&class_name, args);
            }
            ReplCommand::GetField(ref_name, field_name, class_name) => {
                self.get_field_value(&ref_name, &field_name, &class_name);
            }
            ReplCommand::SetField(ref_name, field_name, class_name, value) => {
                self.set_field_value(&ref_name, &field_name, &class_name, &value);
            }
            ReplCommand::ShowState => {
                self.show_state();
            }
            ReplCommand::Help => {
                self.show_help();
            }
            ReplCommand::Exit => {
                println!("Goodbye!");
                self.running = false;
            }
            ReplCommand::Unknown(input) => {
                if !input.is_empty() {
                    eprintln!("Unknown command: {}", input);
                    println!("Type 'help' for available commands");
                }
            }
        }
    }

    /// Show class information
    fn show_class_info(&self, class_name: &str) {
        match self.interpreter.get_class_info(class_name) {
            Ok(info) => {
                println!("\nClass: {}", info.name);
                println!("Super class: {}", info.super_class);
                println!("Access flags: {}", info.access_flags);

                println!("\nFields:");
                for field in &info.fields {
                    println!("  {}", field);
                }

                println!("\nMethods:");
                for method in &info.methods {
                    println!("  {}", method);
                }
            }
            Err(e) => eprintln!("Error getting class info: {:?}", e),
        }
    }

    /// Show method information
    fn show_method_info(&self, class_name: &str, method_name: &str) {
        match self.interpreter.get_method_info(class_name, method_name) {
            Ok(info) => {
                println!("\nMethod: {}", info.name);
                println!("Descriptor: {}", info.descriptor);
                println!("Access flags: {}", info.access_flags);

                if let Some(code_len) = info.code_length {
                    println!("Code length: {} bytes", code_len);
                }
            }
            Err(e) => eprintln!("Error getting method info: {:?}", e),
        }
    }

    /// Show field information
    fn show_field_info(&self, class_name: &str, field_name: &str) {
        match self.interpreter.get_field_info(class_name, field_name) {
            Ok(info) => {
                println!("\nField: {}", info.name);
                println!("Descriptor: {}", info.descriptor);
                println!("Access flags: {}", info.access_flags);
            }
            Err(e) => eprintln!("Error getting field info: {:?}", e),
        }
    }

    /// Execute a method
    fn execute_method(&mut self, class_name: &str, method_name: &str, args: Vec<String>) {
        // Load the class first
        if let Err(e) = self.interpreter.load_class_by_name(class_name) {
            eprintln!("Error loading class '{}': {:?}", class_name, e);
            return;
        }

        if method_name == "main" || args.is_empty() {
            match self.interpreter.run_main(class_name) {
                Ok(_) => println!("Execution completed successfully."),
                Err(e) => eprintln!("Execution error: {:?}", e),
            }
        } else {
            println!(
                "Executing {}.{} with args: {:?}",
                class_name, method_name, args
            );
            // For non-main methods, attempt to run main as fallback
            match self.interpreter.run_main(class_name) {
                Ok(_) => println!("Execution completed."),
                Err(e) => eprintln!("Execution error: {:?}", e),
            }
        }
    }

    /// Create a new instance
    fn new_instance(&mut self, class_name: &str, _args: Vec<String>) {
        match self.interpreter.new_instance(class_name, &[]) {
            Ok(obj) => {
                let addr = obj.as_reference().unwrap_or(0);
                println!("Created instance of {} at address 0x{:x}", class_name, addr);
                // Store in named object registry
                self.named_objects.insert(
                    format!("obj_{:x}", addr),
                    addr,
                );
            }
            Err(e) => eprintln!("Error creating instance: {:?}", e),
        }
    }

    /// Get a field value
    fn get_field_value(&mut self, ref_name: &str, field_name: &str, _class_name: &str) {
        if let Some(&addr) = self.named_objects.get(ref_name) {
            let obj_ref = crate::memory::Value::Reference(addr);
            match self.interpreter.get_field_value(&obj_ref, field_name) {
                Ok(val) => println!("{}.{} = {:?}", ref_name, field_name, val),
                Err(e) => eprintln!("Error getting field: {:?}", e),
            }
        } else {
            eprintln!(
                "Unknown object reference '{}'. Use 'new <class>' to create objects.",
                ref_name
            );
        }
    }

    /// Set a field value
    fn set_field_value(&mut self, ref_name: &str, field_name: &str, _class_name: &str, value: &str) {
        if let Some(&addr) = self.named_objects.get(ref_name) {
            let parsed_value = Self::parse_value(value);
            let obj_ref = crate::memory::Value::Reference(addr);
            match self.interpreter.set_field_value(&obj_ref, field_name, parsed_value) {
                Ok(_) => println!("Set {}.{} = {}", ref_name, field_name, value),
                Err(e) => eprintln!("Error setting field: {:?}", e),
            }
        } else {
            eprintln!(
                "Unknown object reference '{}'. Use 'new <class>' to create objects.",
                ref_name
            );
        }
    }

    /// Parse a string value into a JVM Value
    fn parse_value(s: &str) -> crate::memory::Value {
        if let Ok(i) = s.parse::<i32>() {
            return crate::memory::Value::Int(i);
        }
        if let Ok(l) = s.parse::<i64>() {
            return crate::memory::Value::Long(l);
        }
        if let Ok(f) = s.parse::<f32>() {
            return crate::memory::Value::Float(f);
        }
        if let Ok(d) = s.parse::<f64>() {
            return crate::memory::Value::Double(d);
        }
        if s == "null" {
            return crate::memory::Value::Null;
        }
        if s == "true" {
            return crate::memory::Value::Int(1);
        }
        if s == "false" {
            return crate::memory::Value::Int(0);
        }
        // Default: treat as string reference (allocate on heap)
        crate::memory::Value::Null
    }

    /// Show current JVM state
    fn show_state(&self) {
        println!("\nJVM State:");
        println!(
            "Loaded classes: {}",
            self.interpreter.get_loaded_class_count()
        );
        println!(
            "Heap objects: {}",
            self.interpreter.memory().heap.object_count()
        );
        println!(
            "Memory usage: {} bytes",
            self.interpreter.get_memory_usage()
        );
        println!(
            "Named objects tracked: {}",
            self.named_objects.len()
        );
        if !self.named_objects.is_empty() {
            println!("  Objects:");
            for (name, addr) in &self.named_objects {
                println!("    {} -> 0x{:x}", name, addr);
            }
        }
    }

    /// Show help information
    fn show_help(&self) {
        println!("\nAvailable Commands:");
        println!("  load <class>          - Load a class");
        println!("  class <class>         - Show class information");
        println!("  method <class> <name> - Show method information");
        println!("  field <class> <name>  - Show field information");
        println!("  exec <class> [method] - Execute a method (default: main)");
        println!("  new <class> [args...] - Create a new instance");
        println!("  get <ref> <field> <class> - Get a field value");
        println!("  set <ref> <field> <class> <value> - Set a field value");
        println!("  state                - Show current JVM state");
        println!("  help                 - Show this help message");
        println!("  exit                 - Exit the REPL\n");
    }
}

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

/// Helper functions for Interpreter that would be needed for REPL
trait ReplInterpreterExt {
    fn get_class_info(&self, class_name: &str) -> Result<ClassInfo, JvmError>;
    fn get_method_info(&self, class_name: &str, method_name: &str) -> Result<MethodInfo, JvmError>;
    fn get_field_info(&self, class_name: &str, field_name: &str) -> Result<FieldInfo, JvmError>;
    fn get_loaded_class_count(&self) -> usize;
    fn get_memory_usage(&self) -> usize;
    fn get_gc_count(&self) -> usize;
}

/// Class information
#[derive(Debug, Clone)]
pub struct ClassInfo {
    pub name: String,
    pub super_class: String,
    pub access_flags: String,
    pub fields: Vec<String>,
    pub methods: Vec<String>,
}

/// Method information
#[derive(Debug, Clone)]
pub struct MethodInfo {
    pub name: String,
    pub descriptor: String,
    pub access_flags: String,
    pub code_length: Option<usize>,
}

/// Field information
#[derive(Debug, Clone)]
pub struct FieldInfo {
    pub name: String,
    pub descriptor: String,
    pub access_flags: String,
}

// Note: These are placeholder implementations
// Actual implementation would need to be added to the Interpreter struct

impl ReplInterpreterExt for Interpreter {
    fn get_class_info(&self, class_name: &str) -> Result<ClassInfo, JvmError> {
        // Stub implementation - to be completed
        let class = self.class_loader.get_class(class_name)
            .ok_or_else(|| JvmError::RuntimeError(RuntimeError::ClassNotFound(class_name.to_string())))?;
        
        let super_class_name = class.get_super_class_name().unwrap_or_else(|| "java/lang/Object".to_string());
        
        let fields: Vec<String> = class.fields.iter().filter_map(|f| {
            let name = class.get_string(f.name_index)?;
            let desc = class.get_string(f.descriptor_index)?;
            Some(format!("{} {}", name, desc))
        }).collect();
        
        let methods: Vec<String> = class.methods.iter().filter_map(|m| {
            let name = class.get_string(m.name_index)?;
            let desc = class.get_string(m.descriptor_index)?;
            Some(format!("{} {}", name, desc))
        }).collect();
        
        Ok(ClassInfo {
            name: class_name.to_string(),
            super_class: super_class_name,
            access_flags: format!("0x{:04x}", class.access_flags),
            fields,
            methods,
        })
    }

    fn get_method_info(&self, class_name: &str, method_name: &str) -> Result<MethodInfo, JvmError> {
        // Stub implementation - to be completed
        let class = self.class_loader.get_class(class_name)
            .ok_or_else(|| JvmError::RuntimeError(RuntimeError::ClassNotFound(class_name.to_string())))?;
        
        let method = class.methods.iter()
            .find(|m| {
                class.get_string(m.name_index).as_deref() == Some(method_name)
            })
            .ok_or_else(|| JvmError::RuntimeError(RuntimeError::MethodNotFound(class_name.to_string(), method_name.to_string())))?;
        
        let name = class.get_string(method.name_index).unwrap_or_default();
        let descriptor = class.get_string(method.descriptor_index).unwrap_or_default();
        
        // Find code attribute  
        let code_length = method.attributes.iter().find_map(|attr| {
            if class.get_string(attr.attribute_name_index).as_deref() == Some("Code") {
                // Code attribute format: max_stack (2) + max_locals (2) + code_length (4) + code
                if attr.info.len() >= 8 {
                    let code_len = u32::from_be_bytes([attr.info[4], attr.info[5], attr.info[6], attr.info[7]]) as usize;
                    Some(code_len)
                } else {
                    None
                }
            } else {
                None
            }
        });
        
        Ok(MethodInfo {
            name,
            descriptor,
            access_flags: format!("0x{:04x}", method.access_flags),
            code_length,
        })
    }

    fn get_field_info(&self, class_name: &str, field_name: &str) -> Result<FieldInfo, JvmError> {
        // Stub implementation - to be completed
        let class = self.class_loader.get_class(class_name)
            .ok_or_else(|| JvmError::RuntimeError(RuntimeError::ClassNotFound(class_name.to_string())))?;
        
        let field = class.fields.iter()
            .find(|f| {
                class.get_string(f.name_index).as_deref() == Some(field_name)
            })
            .ok_or_else(|| JvmError::RuntimeError(RuntimeError::FieldNotFound(class_name.to_string(), field_name.to_string())))?;
        
        let name = class.get_string(field.name_index).unwrap_or_default();
        let descriptor = class.get_string(field.descriptor_index).unwrap_or_default();
        
        Ok(FieldInfo {
            name,
            descriptor,
            access_flags: format!("0x{:04x}", field.access_flags),
        })
    }

    fn get_loaded_class_count(&self) -> usize {
        self.get_loaded_class_count()
    }

    fn get_memory_usage(&self) -> usize {
        self.get_memory_usage()
    }

    fn get_gc_count(&self) -> usize {
        self.get_gc_count()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_load_command() {
        let repl = JvmRepl::new();
        let command = repl.parse_command("load java/lang/String");

        assert_eq!(
            command,
            ReplCommand::LoadClass("java/lang/String".to_string())
        );
    }

    #[test]
    fn test_parse_show_class_command() {
        let repl = JvmRepl::new();
        let command = repl.parse_command("class java/lang/Object");

        assert_eq!(
            command,
            ReplCommand::ShowClass("java/lang/Object".to_string())
        );
    }

    #[test]
    fn test_parse_execute_command() {
        let repl = JvmRepl::new();
        let command = repl.parse_command("exec com/example/Main main arg1 arg2");

        assert_eq!(
            command,
            ReplCommand::Execute(
                "com/example/Main".to_string(),
                "main".to_string(),
                vec!["arg1".to_string(), "arg2".to_string()]
            )
        );
    }

    #[test]
    fn test_parse_help_command() {
        let repl = JvmRepl::new();
        let command = repl.parse_command("help");

        assert_eq!(command, ReplCommand::Help);
    }

    #[test]
    fn test_parse_exit_command() {
        let repl = JvmRepl::new();
        let command = repl.parse_command("exit");

        assert_eq!(command, ReplCommand::Exit);

        let command = repl.parse_command("quit");
        assert_eq!(command, ReplCommand::Exit);
    }

    #[test]
    fn test_parse_unknown_command() {
        let repl = JvmRepl::new();
        let command = repl.parse_command("unknown command");

        assert_eq!(command, ReplCommand::Unknown("unknown command".to_string()));
    }
}
