use arc_swap::ArcSwap;
use parking_lot::Mutex;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::sync::Arc;

use rand::rngs::StdRng;
use rand::SeedableRng;

use crate::model::{ScriptEngine, StepContext, Value};
use crate::state::{EditorContext, StackCache};

pub fn update_cache(editor_ctx: &EditorContext) {
    let lines = editor_ctx.editor.lines();
    let cursor_line = editor_ctx.editor.cursor().0;

    let mut hasher = DefaultHasher::new();
    for (i, line) in lines.iter().enumerate() {
        if i > cursor_line {
            break;
        }
        line.hash(&mut hasher);
    }
    let lines_hash = hasher.finish();

    if let Some(ref c) = *editor_ctx.stack_cache.borrow() {
        if c.cursor_line == cursor_line && c.lines_hash == lines_hash {
            return;
        }
    }

    let partial: Vec<&str> = lines
        .iter()
        .take(cursor_line + 1)
        .map(|s| s.as_str())
        .collect();
    let script = partial.join("\n");

    let result = if script.trim().is_empty() {
        "Stack: []".to_string()
    } else {
        let vars = Arc::new(ArcSwap::from_pointee(HashMap::new()));
        let dict = Arc::new(Mutex::new(HashMap::new()));
        let rng = Arc::new(Mutex::new(StdRng::seed_from_u64(42)));
        let engine = ScriptEngine::new(vars, dict, rng);

        let ctx = StepContext {
            step: 0,
            beat: 0.0,
            bank: 0,
            pattern: 0,
            tempo: 120.0,
            phase: 0.0,
            slot: 0,
            runs: 0,
            iter: 0,
            speed: 1.0,
            fill: false,
            nudge_secs: 0.0,
            cc_access: None,
            speed_key: "",
            mouse_x: 0.5,
            mouse_y: 0.5,
            mouse_down: 0.0,
        };

        match engine.evaluate(&script, &ctx) {
            Ok(_) => {
                let stack = engine.stack();
                let formatted: Vec<String> = stack.iter().map(format_value).collect();
                format!("Stack: [{}]", formatted.join(" "))
            }
            Err(e) => format!("Error: {e}"),
        }
    };

    *editor_ctx.stack_cache.borrow_mut() = Some(StackCache {
        cursor_line,
        lines_hash,
        result,
    });
}

fn format_value(v: &Value) -> String {
    match v {
        Value::Int(n, _) => n.to_string(),
        Value::Float(f, _) => {
            if f.fract() == 0.0 && f.abs() < 1_000_000.0 {
                format!("{f:.1}")
            } else {
                format!("{f:.4}")
            }
        }
        Value::Str(s, _) => format!("\"{s}\""),
        Value::Quotation(..) => "[...]".to_string(),
        Value::CycleList(items) | Value::ArpList(items) => {
            let inner: Vec<String> = items.iter().map(format_value).collect();
            format!("({})", inner.join(" "))
        }
    }
}
