//! Interactive debugger for Lux
//!
//! Provides breakpoints, stepping, and variable inspection.

use crate::ast::{Program, Span};
use crate::interpreter::{Interpreter, Value};
use crate::parser::Parser;
use crate::typechecker::TypeChecker;
use crate::modules::ModuleLoader;
use std::collections::{HashMap, HashSet};
use std::io::{self, Write};
use std::path::Path;

/// Debugger state
pub struct Debugger {
    /// Breakpoints by line number
    breakpoints: HashSet<usize>,
    /// Whether to stop at next statement (single-step mode)
    single_step: bool,
    /// Current call stack for display
    call_stack: Vec<String>,
    /// Variable values at current scope
    variables: HashMap<String, String>,
    /// Source code lines
    source_lines: Vec<String>,
    /// Current line being executed
    current_line: usize,
    /// File being debugged
    file_path: String,
}

impl Debugger {
    pub fn new(source: &str, file_path: &str) -> Self {
        Self {
            breakpoints: HashSet::new(),
            single_step: false,
            call_stack: vec!["<main>".to_string()],
            variables: HashMap::new(),
            source_lines: source.lines().map(String::from).collect(),
            current_line: 1,
            file_path: file_path.to_string(),
        }
    }

    /// Run the debugger on a file
    pub fn run(path: &str) -> Result<(), String> {
        let file_path = Path::new(path);
        let source = std::fs::read_to_string(file_path)
            .map_err(|e| format!("Error reading file '{}': {}", path, e))?;

        let mut debugger = Debugger::new(&source, path);
        debugger.debug_session(&source, file_path)
    }

    fn debug_session(&mut self, source: &str, file_path: &Path) -> Result<(), String> {
        println!("Lux Debugger");
        println!("Type 'help' for available commands.");
        println!();

        // Parse and type check the program
        let mut loader = ModuleLoader::new();
        if let Some(parent) = file_path.parent() {
            loader.add_search_path(parent.to_path_buf());
        }

        let program = loader.load_source(source, Some(file_path))
            .map_err(|e| format!("Parse error: {}", e))?;

        let mut checker = TypeChecker::new();
        checker.check_program_with_modules(&program, &loader)
            .map_err(|errors| {
                errors.iter()
                    .map(|e| e.to_string())
                    .collect::<Vec<_>>()
                    .join("\n")
            })?;

        // Show initial state
        self.show_source_context(1);

        // Enter debug REPL
        self.debug_repl(&program, &loader)
    }

    fn debug_repl(&mut self, program: &Program, loader: &ModuleLoader) -> Result<(), String> {
        let mut input = String::new();

        loop {
            print!("(lux-debug) ");
            io::stdout().flush().unwrap();

            input.clear();
            if io::stdin().read_line(&mut input).is_err() {
                break;
            }

            let cmd = input.trim();
            if cmd.is_empty() {
                continue;
            }

            match self.handle_command(cmd, program, loader) {
                Ok(true) => continue,
                Ok(false) => break,
                Err(e) => println!("Error: {}", e),
            }
        }

        Ok(())
    }

    fn handle_command(&mut self, cmd: &str, program: &Program, loader: &ModuleLoader) -> Result<bool, String> {
        let parts: Vec<&str> = cmd.split_whitespace().collect();
        if parts.is_empty() {
            return Ok(true);
        }

        match parts[0] {
            "help" | "h" => {
                self.show_help();
            }
            "quit" | "q" => {
                return Ok(false);
            }
            "run" | "r" => {
                self.run_program(program, loader)?;
            }
            "break" | "b" => {
                if parts.len() < 2 {
                    println!("Usage: break <line_number>");
                } else if let Ok(line) = parts[1].parse::<usize>() {
                    self.add_breakpoint(line);
                } else {
                    println!("Invalid line number: {}", parts[1]);
                }
            }
            "delete" | "d" => {
                if parts.len() < 2 {
                    println!("Usage: delete <line_number>");
                } else if let Ok(line) = parts[1].parse::<usize>() {
                    self.remove_breakpoint(line);
                } else {
                    println!("Invalid line number: {}", parts[1]);
                }
            }
            "list" | "l" => {
                let line = if parts.len() > 1 {
                    parts[1].parse().unwrap_or(self.current_line)
                } else {
                    self.current_line
                };
                self.show_source_context(line);
            }
            "breakpoints" | "bp" => {
                self.list_breakpoints();
            }
            "step" | "s" => {
                self.single_step = true;
                println!("Single-step mode enabled. Use 'run' to execute.");
            }
            "continue" | "c" => {
                self.single_step = false;
                println!("Continue mode. Will stop at next breakpoint.");
            }
            "print" | "p" => {
                if parts.len() < 2 {
                    println!("Usage: print <expression>");
                } else {
                    let expr_str = parts[1..].join(" ");
                    self.eval_expression(&expr_str)?;
                }
            }
            "locals" | "vars" => {
                self.show_variables();
            }
            "stack" | "bt" => {
                self.show_call_stack();
            }
            _ => {
                println!("Unknown command: {}. Type 'help' for available commands.", parts[0]);
            }
        }

        Ok(true)
    }

    fn show_help(&self) {
        println!("Debugger Commands:");
        println!("  help, h              Show this help");
        println!("  quit, q              Exit debugger");
        println!("  run, r               Run/continue program");
        println!("  break <line>, b      Set breakpoint at line");
        println!("  delete <line>, d     Remove breakpoint");
        println!("  breakpoints, bp      List all breakpoints");
        println!("  list [line], l       Show source around line");
        println!("  step, s              Enable single-step mode");
        println!("  continue, c          Disable single-step mode");
        println!("  print <expr>, p      Evaluate and print expression");
        println!("  locals, vars         Show local variables");
        println!("  stack, bt            Show call stack");
    }

    fn add_breakpoint(&mut self, line: usize) {
        if line > 0 && line <= self.source_lines.len() {
            self.breakpoints.insert(line);
            println!("Breakpoint set at line {}", line);
            self.show_line(line);
        } else {
            println!("Line {} is out of range (1-{})", line, self.source_lines.len());
        }
    }

    fn remove_breakpoint(&mut self, line: usize) {
        if self.breakpoints.remove(&line) {
            println!("Breakpoint removed at line {}", line);
        } else {
            println!("No breakpoint at line {}", line);
        }
    }

    fn list_breakpoints(&self) {
        if self.breakpoints.is_empty() {
            println!("No breakpoints set.");
        } else {
            println!("Breakpoints:");
            let mut lines: Vec<_> = self.breakpoints.iter().collect();
            lines.sort();
            for line in lines {
                print!("  ");
                self.show_line(*line);
            }
        }
    }

    fn show_source_context(&self, center_line: usize) {
        let start = center_line.saturating_sub(3).max(1);
        let end = (center_line + 3).min(self.source_lines.len());

        println!();
        for line_num in start..=end {
            self.show_line_with_marker(line_num, line_num == center_line);
        }
        println!();
    }

    fn show_line(&self, line_num: usize) {
        self.show_line_with_marker(line_num, false);
    }

    fn show_line_with_marker(&self, line_num: usize, is_current: bool) {
        if line_num > 0 && line_num <= self.source_lines.len() {
            let bp_marker = if self.breakpoints.contains(&line_num) { "*" } else { " " };
            let cur_marker = if is_current { ">" } else { " " };
            println!(
                "{}{} {:4} | {}",
                bp_marker,
                cur_marker,
                line_num,
                self.source_lines[line_num - 1]
            );
        }
    }

    fn run_program(&mut self, program: &Program, loader: &ModuleLoader) -> Result<(), String> {
        println!("Running {}...", self.file_path);
        println!();

        let mut interp = Interpreter::new();
        match interp.run_with_modules(program, loader) {
            Ok(value) => {
                if !matches!(value, Value::Unit) {
                    println!();
                    println!("Result: {}", value);
                }
                println!();
                println!("Program finished.");
            }
            Err(e) => {
                println!();
                println!("Runtime error: {}", e.message);
                if let Some(span) = e.span {
                    // Try to find the line from the span
                    let line = self.span_to_line(span);
                    self.current_line = line;
                    self.show_source_context(line);
                }
            }
        }

        Ok(())
    }

    fn span_to_line(&self, span: Span) -> usize {
        // Count newlines in source up to span.start
        let source: String = self.source_lines.join("\n");
        let mut line = 1;
        for (i, c) in source.chars().enumerate() {
            if i >= span.start {
                break;
            }
            if c == '\n' {
                line += 1;
            }
        }
        line
    }

    fn eval_expression(&mut self, expr_str: &str) -> Result<(), String> {
        // Parse and evaluate the expression by creating a simple program
        use crate::lexer::Lexer;

        // Create a mini program to evaluate: just the expression as a top-level binding
        let source = format!("let __debug_result__ = {}", expr_str);
        let lexer = Lexer::new(&source);
        let tokens = lexer.tokenize()
            .map_err(|e| format!("Lexer error: {}", e.message))?;
        let mut parser = Parser::new(tokens);
        let program = parser.parse_program()
            .map_err(|e| format!("Parse error: {}", e.message))?;

        let mut interp = Interpreter::new();
        match interp.run(&program) {
            Ok(value) => {
                println!("{}", value);
            }
            Err(e) => {
                println!("Error: {}", e.message);
            }
        }

        Ok(())
    }

    fn show_variables(&self) {
        if self.variables.is_empty() {
            println!("No variables in current scope.");
            println!("(Run the program first to capture variable state)");
        } else {
            println!("Local variables:");
            for (name, value) in &self.variables {
                println!("  {} = {}", name, value);
            }
        }
    }

    fn show_call_stack(&self) {
        println!("Call stack:");
        for (i, frame) in self.call_stack.iter().enumerate().rev() {
            let marker = if i == self.call_stack.len() - 1 { ">" } else { " " };
            println!("  {} #{} {}", marker, i, frame);
        }
    }
}
