use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::PathBuf;
use std::rc::Rc;
use std::cell::RefCell;

use super::lexer;
use super::parser::{self, Expr, Stmt, BinaryOp, UnaryOp};
use super::value::Value;

pub struct Interpreter {
    globals: Rc<RefCell<HashMap<String, Value>>>,
    env: Rc<RefCell<HashMap<String, Value>>>,
    imported: Rc<RefCell<HashSet<PathBuf>>>,
    base_path: PathBuf,
}

#[derive(Debug, Clone)]
enum ControlFlow {
    Return(Value),
    Break,
    Continue,
}

impl Interpreter {
    pub fn new() -> Self {
        let globals = Rc::new(RefCell::new(HashMap::new()));
        Self::register_builtins(&mut globals.borrow_mut());
        Interpreter {
            globals: globals.clone(),
            env: globals,
            imported: Rc::new(RefCell::new(HashSet::new())),
            base_path: PathBuf::from("."),
        }
    }

    pub fn with_base_path(mut self, path: &str) -> Self {
        self.base_path = PathBuf::from(path);
        self
    }

    pub fn mark_imported(&mut self, path: &str) {
        let file_path = self.base_path.join(path);
        if let Ok(canonical) = fs::canonicalize(&file_path) {
            self.imported.borrow_mut().insert(canonical);
        } else {
            self.imported.borrow_mut().insert(file_path);
        }
    }

    fn register_builtins(env: &mut HashMap<String, Value>) {
        env.insert("print".to_string(), Value::Builtin(builtin_print));
        env.insert("input".to_string(), Value::Builtin(builtin_input));
        env.insert("len".to_string(), Value::Builtin(builtin_len));
        env.insert("typeof".to_string(), Value::Builtin(builtin_typeof));
        env.insert("push".to_string(), Value::Builtin(builtin_push));
        env.insert("pop".to_string(), Value::Builtin(builtin_pop));
        env.insert("keys".to_string(), Value::Builtin(builtin_keys));
        env.insert("values".to_string(), Value::Builtin(builtin_values));
        env.insert("range".to_string(), Value::Builtin(builtin_range));
        env.insert("str".to_string(), Value::Builtin(builtin_str));
        env.insert("num".to_string(), Value::Builtin(builtin_num));
    }

    pub fn execute(&mut self, stmts: &[Stmt]) -> Result<Value, String> {
        let mut last = Value::Null;
        for stmt in stmts {
            match self.execute_stmt(stmt)? {
                Some(ControlFlow::Return(v)) => return Ok(v),
                Some(ControlFlow::Break) => return Err("break outside loop".to_string()),
                Some(ControlFlow::Continue) => return Err("continue outside loop".to_string()),
                None => {}
            }
        }
        Ok(last)
    }

    fn execute_stmt(&mut self, stmt: &Stmt) -> Result<Option<ControlFlow>, String> {
        match stmt {
            Stmt::Import { path } => {
                let file_path = self.base_path.join(path);
                let canonical = fs::canonicalize(&file_path).unwrap_or(file_path.clone());
                
                if self.imported.borrow().contains(&canonical) {
                    return Ok(None);
                }
                
                self.imported.borrow_mut().insert(canonical.clone());
                
                let source = fs::read_to_string(&file_path)
                    .map_err(|e| format!("Failed to import '{}': {}", path, e))?;
                
                let tokens = lexer::tokenize(&source)?;
                let ast = parser::parse(&tokens)?;
                
                let new_base = canonical.parent().unwrap_or(&self.base_path).to_path_buf();
                let mut sub_interp = Interpreter {
                    globals: self.globals.clone(),
                    env: self.env.clone(),
                    imported: self.imported.clone(),
                    base_path: new_base,
                };
                sub_interp.execute(&ast)?;
                
                Ok(None)
            }
            Stmt::Expr(expr) => {
                self.evaluate(expr)?;
                Ok(None)
            }
            Stmt::Let { name, value } => {
                let val = self.evaluate(value)?;
                self.env.borrow_mut().insert(name.clone(), val);
                Ok(None)
            }
            Stmt::Const { name, value } => {
                let val = self.evaluate(value)?;
                self.env.borrow_mut().insert(name.clone(), val);
                Ok(None)
            }
            Stmt::Fn { name, params, body } => {
                let func = Value::Function {
                    params: params.clone(),
                    body: body.clone(),
                    closure: self.env.clone(),
                };
                self.env.borrow_mut().insert(name.clone(), func);
                Ok(None)
            }
            Stmt::Block(stmts) => {
                for stmt in stmts {
                    match self.execute_stmt(stmt)? {
                        Some(cf) => return Ok(Some(cf)),
                        None => {}
                    }
                }
                Ok(None)
            }
            Stmt::If { condition, then_branch, else_branch } => {
                if self.evaluate(condition)?.is_truthy() {
                    for stmt in then_branch {
                        match self.execute_stmt(stmt)? {
                            Some(cf) => return Ok(Some(cf)),
                            None => {}
                        }
                    }
                } else if let Some(else_stmts) = else_branch {
                    for stmt in else_stmts {
                        match self.execute_stmt(stmt)? {
                            Some(cf) => return Ok(Some(cf)),
                            None => {}
                        }
                    }
                }
                Ok(None)
            }
            Stmt::While { condition, body } => {
                loop {
                    if !self.evaluate(condition)?.is_truthy() {
                        break;
                    }
                    for stmt in body {
                        match self.execute_stmt(stmt)? {
                            Some(ControlFlow::Return(v)) => return Ok(Some(ControlFlow::Return(v))),
                            Some(ControlFlow::Break) => return Ok(None),
                            Some(ControlFlow::Continue) => break,
                            None => {}
                        }
                    }
                }
                Ok(None)
            }
            Stmt::For { var, iterable, body } => {
                let iter_val = self.evaluate(iterable)?;
                let items = match iter_val {
                    Value::Array(arr) => arr.borrow().clone(),
                    Value::String(s) => s.chars().map(|c| Value::String(c.to_string())).collect(),
                    _ => return Err("Cannot iterate over this type".to_string()),
                };
                for item in items {
                    self.env.borrow_mut().insert(var.clone(), item);
                    for stmt in body {
                        match self.execute_stmt(stmt)? {
                            Some(ControlFlow::Return(v)) => return Ok(Some(ControlFlow::Return(v))),
                            Some(ControlFlow::Break) => return Ok(None),
                            Some(ControlFlow::Continue) => break,
                            None => {}
                        }
                    }
                }
                Ok(None)
            }
            Stmt::Return(expr) => {
                let val = match expr {
                    Some(e) => self.evaluate(e)?,
                    None => Value::Null,
                };
                Ok(Some(ControlFlow::Return(val)))
            }
            Stmt::Break => Ok(Some(ControlFlow::Break)),
            Stmt::Continue => Ok(Some(ControlFlow::Continue)),
        }
    }

    fn evaluate(&mut self, expr: &Expr) -> Result<Value, String> {
        match expr {
            Expr::Null => Ok(Value::Null),
            Expr::Bool(b) => Ok(Value::Bool(*b)),
            Expr::Number(n) => Ok(Value::Number(*n)),
            Expr::String(s) => Ok(Value::String(s.clone())),
            Expr::Identifier(name) => {
                if let Some(val) = self.env.borrow().get(name) {
                    return Ok(val.clone());
                }
                if let Some(val) = self.globals.borrow().get(name) {
                    return Ok(val.clone());
                }
                Err(format!("Undefined variable: '{}'", name))
            }
            Expr::Array(elements) => {
                let mut vals = Vec::new();
                for elem in elements {
                    vals.push(self.evaluate(elem)?);
                }
                Ok(Value::Array(Rc::new(RefCell::new(vals))))
            }
            Expr::Object(pairs) => {
                let mut map = HashMap::new();
                for (k, v) in pairs {
                    map.insert(k.clone(), self.evaluate(v)?);
                }
                Ok(Value::Object(Rc::new(RefCell::new(map))))
            }
            Expr::Binary { left, op, right } => {
                let l = self.evaluate(left)?;
                let r = self.evaluate(right)?;
                match op {
                    BinaryOp::Add => {
                        match (&l, &r) {
                            (Value::String(a), _) => Ok(Value::String(format!("{}{}", a, r))),
                            (_, Value::String(b)) => Ok(Value::String(format!("{}{}", l, b))),
                            (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
                            _ => Err("Invalid operands for +".to_string()),
                        }
                    }
                    BinaryOp::Sub => numeric_op(l, r, |a, b| a - b),
                    BinaryOp::Mul => numeric_op(l, r, |a, b| a * b),
                    BinaryOp::Div => numeric_op(l, r, |a, b| a / b),
                    BinaryOp::Mod => numeric_op(l, r, |a, b| a % b),
                    BinaryOp::Eq => Ok(Value::Bool(l == r)),
                    BinaryOp::Neq => Ok(Value::Bool(l != r)),
                    BinaryOp::Lt => compare_op(l, r, |a, b| a < b),
                    BinaryOp::Gt => compare_op(l, r, |a, b| a > b),
                    BinaryOp::Lte => compare_op(l, r, |a, b| a <= b),
                    BinaryOp::Gte => compare_op(l, r, |a, b| a >= b),
                    BinaryOp::And => Ok(Value::Bool(l.is_truthy() && r.is_truthy())),
                    BinaryOp::Or => Ok(Value::Bool(l.is_truthy() || r.is_truthy())),
                }
            }
            Expr::Unary { op, expr } => {
                let val = self.evaluate(expr)?;
                match op {
                    UnaryOp::Neg => match val {
                        Value::Number(n) => Ok(Value::Number(-n)),
                        _ => Err("Cannot negate non-number".to_string()),
                    },
                    UnaryOp::Not => Ok(Value::Bool(!val.is_truthy())),
                }
            }
            Expr::Call { callee, args } => {
                let func = self.evaluate(callee)?;
                let arg_vals: Result<Vec<Value>, String> = args.iter().map(|a| self.evaluate(a)).collect();
                let arg_vals = arg_vals?;
                
                match func {
                    Value::Builtin(f) => f(&arg_vals),
                    Value::Function { params, body, closure } => {
                        let mut new_env = HashMap::new();
                        for (k, v) in closure.borrow().iter() {
                            new_env.insert(k.clone(), v.clone());
                        }
                        for (i, param) in params.iter().enumerate() {
                            new_env.insert(param.clone(), arg_vals.get(i).cloned().unwrap_or(Value::Null));
                        }
                        let old_env = self.env.clone();
                        self.env = Rc::new(RefCell::new(new_env));
                        let mut result = Value::Null;
                        for stmt in &body {
                            match self.execute_stmt(stmt)? {
                                Some(ControlFlow::Return(v)) => { result = v; break; }
                                Some(ControlFlow::Break) => { result = Value::Null; break; }
                                Some(ControlFlow::Continue) => { result = Value::Null; break; }
                                None => {}
                            }
                        }
                        self.env = old_env;
                        Ok(result)
                    }
                    _ => Err("Not a function".to_string()),
                }
            }
            Expr::Index { object, index } => {
                let obj = self.evaluate(object)?;
                let idx = self.evaluate(index)?;
                match (obj, idx) {
                    (Value::Array(arr), Value::Number(n)) => {
                        let i = n as usize;
                        let arr = arr.borrow();
                        if i >= arr.len() {
                            return Err("Index out of bounds".to_string());
                        }
                        Ok(arr[i].clone())
                    }
                    (Value::String(s), Value::Number(n)) => {
                        let i = n as usize;
                        if i >= s.len() {
                            return Err("Index out of bounds".to_string());
                        }
                        Ok(Value::String(s.chars().nth(i).unwrap().to_string()))
                    }
                    (Value::Object(map), Value::String(key)) => {
                        let map = map.borrow();
                        match map.get(&key) {
                            Some(v) => Ok(v.clone()),
                            None => Ok(Value::Null),
                        }
                    }
                    _ => Err("Cannot index this type".to_string()),
                }
            }
            Expr::Member { object, property } => {
                let obj = self.evaluate(object)?;
                match obj {
                    Value::Object(map) => {
                        let map = map.borrow();
                        match map.get(property) {
                            Some(v) => Ok(v.clone()),
                            None => Ok(Value::Null),
                        }
                    }
                    _ => Err("Cannot access property".to_string()),
                }
            }
            Expr::Assign { target, value } => {
                let val = self.evaluate(value)?;
                match target.as_ref() {
                    Expr::Identifier(name) => {
                        self.env.borrow_mut().insert(name.clone(), val.clone());
                        Ok(val)
                    }
                    Expr::Member { object, property } => {
                        let obj = self.evaluate(object)?;
                        match obj {
                            Value::Object(map) => {
                                map.borrow_mut().insert(property.clone(), val.clone());
                                Ok(val)
                            }
                            _ => Err("Cannot set property".to_string()),
                        }
                    }
                    Expr::Index { object, index } => {
                        let obj = self.evaluate(object)?;
                        let idx = self.evaluate(index)?;
                        match (obj, idx) {
                            (Value::Array(arr), Value::Number(n)) => {
                                let i = n as usize;
                                arr.borrow_mut()[i] = val.clone();
                                Ok(val)
                            }
                            (Value::Object(map), Value::String(key)) => {
                                map.borrow_mut().insert(key, val.clone());
                                Ok(val)
                            }
                            _ => Err("Cannot set index".to_string()),
                        }
                    }
                    _ => Err("Invalid assignment target".to_string()),
                }
            }
            Expr::Lambda { params, body } => {
                Ok(Value::Function {
                    params: params.clone(),
                    body: body.clone(),
                    closure: self.env.clone(),
                })
            }
        }
    }
}

fn numeric_op<F>(left: Value, right: Value, op: F) -> Result<Value, String>
where F: Fn(f64, f64) -> f64 {
    match (left, right) {
        (Value::Number(a), Value::Number(b)) => Ok(Value::Number(op(a, b))),
        _ => Err("Invalid operands for numeric operation".to_string()),
    }
}

fn compare_op<F>(left: Value, right: Value, op: F) -> Result<Value, String>
where F: Fn(f64, f64) -> bool {
    match (left, right) {
        (Value::Number(a), Value::Number(b)) => Ok(Value::Bool(op(a, b))),
        _ => Err("Invalid operands for comparison".to_string()),
    }
}

fn builtin_print(args: &[Value]) -> Result<Value, String> {
    let msg = args.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(" ");
    println!("{}", msg);
    Ok(Value::Null)
}

fn builtin_input(args: &[Value]) -> Result<Value, String> {
    use std::io::{self, Write};
    if let Some(arg) = args.first() {
        print!("{}", arg);
        io::stdout().flush().unwrap();
    }
    let mut buf = String::new();
    io::stdin().read_line(&mut buf).map_err(|e| e.to_string())?;
    Ok(Value::String(buf.trim().to_string()))
}

fn builtin_len(args: &[Value]) -> Result<Value, String> {
    match args.first() {
        Some(Value::Array(arr)) => Ok(Value::Number(arr.borrow().len() as f64)),
        Some(Value::String(s)) => Ok(Value::Number(s.len() as f64)),
        _ => Ok(Value::Number(0.0)),
    }
}

fn builtin_typeof(args: &[Value]) -> Result<Value, String> {
    match args.first() {
        Some(v) => Ok(Value::String(v.type_name().to_string())),
        None => Ok(Value::String("null".to_string())),
    }
}

fn builtin_push(args: &[Value]) -> Result<Value, String> {
    if args.len() < 2 {
        return Err("push requires 2 arguments".to_string());
    }
    match &args[0] {
        Value::Array(arr) => {
            arr.borrow_mut().push(args[1].clone());
            Ok(args[0].clone())
        }
        _ => Err("First argument must be an array".to_string()),
    }
}

fn builtin_pop(args: &[Value]) -> Result<Value, String> {
    match args.first() {
        Some(Value::Array(arr)) => {
            match arr.borrow_mut().pop() {
                Some(v) => Ok(v),
                None => Ok(Value::Null),
            }
        }
        _ => Err("pop requires an array".to_string()),
    }
}

fn builtin_keys(args: &[Value]) -> Result<Value, String> {
    match args.first() {
        Some(Value::Object(map)) => {
            let keys: Vec<Value> = map.borrow().keys().cloned().map(Value::String).collect();
            Ok(Value::Array(Rc::new(RefCell::new(keys))))
        }
        _ => Err("keys requires an object".to_string()),
    }
}

fn builtin_values(args: &[Value]) -> Result<Value, String> {
    match args.first() {
        Some(Value::Object(map)) => {
            let vals: Vec<Value> = map.borrow().values().cloned().collect();
            Ok(Value::Array(Rc::new(RefCell::new(vals))))
        }
        _ => Err("values requires an object".to_string()),
    }
}

fn builtin_range(args: &[Value]) -> Result<Value, String> {
    if args.len() < 2 {
        return Err("range requires 2 arguments".to_string());
    }
    match (&args[0], &args[1]) {
        (Value::Number(start), Value::Number(end)) => {
            let mut vals = Vec::new();
            let s = *start as i64;
            let e = *end as i64;
            for i in s..=e {
                vals.push(Value::Number(i as f64));
            }
            Ok(Value::Array(Rc::new(RefCell::new(vals))))
        }
        _ => Err("range requires numeric arguments".to_string()),
    }
}

fn builtin_str(args: &[Value]) -> Result<Value, String> {
    match args.first() {
        Some(v) => Ok(Value::String(v.to_string())),
        None => Ok(Value::String("".to_string())),
    }
}

fn builtin_num(args: &[Value]) -> Result<Value, String> {
    match args.first() {
        Some(Value::String(s)) => match s.parse::<f64>() {
            Ok(n) => Ok(Value::Number(n)),
            Err(_) => Ok(Value::Number(0.0)),
        },
        Some(Value::Number(n)) => Ok(Value::Number(*n)),
        Some(Value::Bool(b)) => Ok(Value::Number(if *b { 1.0 } else { 0.0 })),
        _ => Ok(Value::Number(0.0)),
    }
}
