use crate::errors::panic;
use crate::value::Value;
use crate::vm::CallFrame;

#[derive(Debug, Clone, PartialEq)]
pub enum PanicMsg {
    NoLetMatch,
    NoFnMatch,
    NoMatch,
    Generic(String),
}

impl std::fmt::Display for PanicMsg {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use PanicMsg::*;
        match self {
            NoLetMatch => write!(f, "no match in `let`"),
            NoFnMatch => write!(f, "no match calling fn"),
            NoMatch => write!(f, "no match in `match` form"),
            Generic(s) => write!(f, "{s}"),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct Panic {
    pub msg: PanicMsg,
    pub scrutinee: Option<Value>,
    pub call_stack: Vec<CallFrame>,
}

fn frame_dump(frame: &CallFrame) -> String {
    let dump = format!("stack name: {}\nspans: {:?}", frame, frame.chunk().spans);
    dump
}

impl std::fmt::Display for Panic {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        let stub_trace = self
            .call_stack
            .iter()
            .map(frame_dump)
            .collect::<Vec<_>>()
            .join("\n");
        write!(f, "Panic: {}\n{stub_trace}", self.msg)
    }
}
