//! MATLAB-compatible `warning` builtin with state management and formatting support.

use once_cell::sync::Lazy;
use runmat_builtins::{CellArray, StructValue, Value};
use runmat_macros::runtime_builtin;
use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::sync::Mutex;

use crate::builtins::common::format::format_variadic;
use crate::builtins::common::spec::{
    BroadcastSemantics, BuiltinFusionSpec, BuiltinGpuSpec, ConstantStrategy, GpuOpKind,
    ReductionNaN, ResidencyPolicy, ShapeRequirements,
};
use crate::builtins::diagnostics::type_resolvers::warning_type;
use crate::console::{record_console_line, ConsoleStream};
use crate::warning_store;
use crate::{build_runtime_error, RuntimeError};
use tracing;

const DEFAULT_IDENTIFIER: &str = "RunMat:warning";

#[runmat_macros::register_gpu_spec(builtin_path = "crate::builtins::diagnostics::warning")]
pub const GPU_SPEC: BuiltinGpuSpec = BuiltinGpuSpec {
    name: "warning",
    op_kind: GpuOpKind::Custom("control"),
    supported_precisions: &[],
    broadcast: BroadcastSemantics::None,
    provider_hooks: &[],
    constant_strategy: ConstantStrategy::InlineLiteral,
    residency: ResidencyPolicy::GatherImmediately,
    nan_mode: ReductionNaN::Include,
    two_pass_threshold: None,
    workgroup_size: None,
    accepts_nan_mode: false,
    notes: "Control-flow builtin; GPU backends are never invoked.",
};

#[runmat_macros::register_fusion_spec(builtin_path = "crate::builtins::diagnostics::warning")]
pub const FUSION_SPEC: BuiltinFusionSpec = BuiltinFusionSpec {
    name: "warning",
    shape: ShapeRequirements::Any,
    constant_strategy: ConstantStrategy::InlineLiteral,
    elementwise: None,
    reduction: None,
    emits_nan: false,
    notes: "Control-flow builtin; excluded from fusion planning.",
};

static MANAGER: Lazy<Mutex<WarningManager>> = Lazy::new(|| Mutex::new(WarningManager::default()));

fn manager() -> &'static Mutex<WarningManager> {
    &MANAGER
}

fn with_manager<F, R>(func: F) -> R
where
    F: FnOnce(&mut WarningManager) -> R,
{
    let mut guard = manager().lock().expect("warning manager mutex poisoned");
    func(&mut guard)
}

fn warning_flow(identifier: &str, message: impl Into<String>) -> RuntimeError {
    build_runtime_error(message)
        .with_builtin("warning")
        .with_identifier(normalize_identifier(identifier))
        .build()
}

fn warning_default_error(message: impl Into<String>) -> RuntimeError {
    warning_flow(DEFAULT_IDENTIFIER, message)
}

fn remap_warning_flow<F>(err: RuntimeError, identifier: &str, message: F) -> RuntimeError
where
    F: FnOnce(&crate::RuntimeError) -> String,
{
    build_runtime_error(message(&err))
        .with_builtin("warning")
        .with_identifier(normalize_identifier(identifier))
        .with_source(err)
        .build()
}

#[runtime_builtin(
    name = "warning",
    category = "diagnostics",
    summary = "Display formatted warnings, control warning state, and query per-identifier settings.",
    keywords = "warning,diagnostics,state,query,backtrace",
    accel = "metadata",
    sink = true,
    suppress_auto_output = true,
    type_resolver(warning_type),
    builtin_path = "crate::builtins::diagnostics::warning"
)]
fn warning_builtin(args: Vec<Value>) -> crate::BuiltinResult<Value> {
    if args.is_empty() {
        return handle_query_default();
    }

    let first = &args[0];
    let rest = &args[1..];

    match first {
        Value::Struct(_) | Value::Cell(_) => {
            if !rest.is_empty() {
                return Err(warning_default_error(
                    "warning: state restoration accepts a single argument",
                ));
            }
            apply_state_value(first)?;
            Ok(Value::Num(0.0))
        }
        Value::MException(mex) => {
            if !rest.is_empty() {
                return Err(warning_default_error(
                    "warning: additional arguments are not allowed when passing an MException",
                ));
            }
            Ok(reissue_exception(mex)?)
        }
        _ => {
            let first_string = value_to_string("warning", first)?;
            if let Some(command) = parse_command(&first_string) {
                return handle_command(command, rest);
            }
            Ok(handle_message_call(None, first_string, rest)?)
        }
    }
}

fn handle_message_call(
    explicit_identifier: Option<String>,
    first_string: String,
    rest: &[Value],
) -> crate::BuiltinResult<Value> {
    if let Some(identifier) = explicit_identifier {
        return emit_warning(&identifier, &first_string, rest);
    }

    if rest.is_empty() {
        emit_warning(DEFAULT_IDENTIFIER, &first_string, rest)
    } else if is_message_identifier(&first_string) {
        let fmt = value_to_string("warning", &rest[0])?;
        let args = &rest[1..];
        emit_warning(&first_string, &fmt, args)
    } else {
        emit_warning(DEFAULT_IDENTIFIER, &first_string, rest)
    }
}

fn emit_warning(identifier_raw: &str, fmt: &str, args: &[Value]) -> crate::BuiltinResult<Value> {
    let identifier = normalize_identifier(identifier_raw);
    let message = format_variadic(fmt, args).map_err(|flow| {
        remap_warning_flow(flow, DEFAULT_IDENTIFIER, |err| err.message().to_string())
    })?;

    let action = with_manager(|mgr| {
        let action = mgr.action_for(&identifier);
        if matches!(action, WarningAction::Display | WarningAction::AsError) {
            mgr.record_last(&identifier, &message);
        }
        action
    });

    match action {
        WarningAction::Suppress => Ok(Value::Num(0.0)),
        WarningAction::Display => {
            print_warning(&identifier, &message);
            warning_store::push(&identifier, &message);
            Ok(Value::Num(0.0))
        }
        WarningAction::AsError => {
            warning_store::push(&identifier, &message);
            Err(warning_flow(&identifier, message))
        }
    }
}

fn print_warning(identifier: &str, message: &str) {
    let (backtrace_enabled, verbose_enabled) =
        with_manager(|mgr| (mgr.backtrace_enabled, mgr.verbose_enabled));

    emit_stderr_line(format!("Warning: {message}"));
    if identifier != DEFAULT_IDENTIFIER {
        emit_stderr_line(format!("identifier: {identifier}"));
    }

    if verbose_enabled {
        let suppression = if identifier == DEFAULT_IDENTIFIER {
            DEFAULT_IDENTIFIER.to_string()
        } else {
            identifier.to_string()
        };
        emit_stderr_line(format!(
            "(Type \"warning('off','{suppression}')\" to suppress this warning.)"
        ));
    }

    if backtrace_enabled {
        let bt = std::backtrace::Backtrace::force_capture();
        emit_stderr_line(format!("{bt}"));
    }
}

fn emit_stderr_line(line: String) {
    tracing::warn!("{line}");
    record_console_line(ConsoleStream::Stderr, line);
}

fn reissue_exception(mex: &runmat_builtins::MException) -> crate::BuiltinResult<Value> {
    let identifier = normalize_identifier(&mex.identifier);
    emit_warning(&identifier, &mex.message, &[])
}

fn handle_command(command: Command, rest: &[Value]) -> crate::BuiltinResult<Value> {
    match command {
        Command::SetMode(mode) => set_mode_command(mode, rest),
        Command::Default => default_command(rest),
        Command::Reset => {
            if !rest.is_empty() {
                return Err(warning_default_error(
                    "warning: 'reset' does not accept additional arguments",
                ));
            }
            with_manager(WarningManager::reset);
            Ok(Value::Num(0.0))
        }
        Command::Query => query_command(rest),
        Command::Status => status_command(rest),
        Command::Backtrace => backtrace_command(rest),
    }
}

fn set_mode_command(mode: WarningMode, rest: &[Value]) -> crate::BuiltinResult<Value> {
    let identifier = if rest.is_empty() {
        "all".to_string()
    } else if rest.len() == 1 {
        value_to_string("warning", &rest[0])?
    } else {
        return Err(warning_default_error(
            "warning: too many input arguments for state change",
        ));
    };

    let trimmed = identifier.trim();
    if trimmed.eq_ignore_ascii_case("all") {
        return with_manager(|mgr| {
            let previous = mgr.default_mode;
            let value = Value::Struct(mgr.state_struct_for("all", WarningRule::new(previous)));
            mgr.set_global_mode(mode);
            Ok(value)
        });
    }

    if trimmed.eq_ignore_ascii_case("last") {
        let last_identifier = with_manager(|mgr| mgr.last_warning.clone());
        let Some((identifier, _)) = last_identifier else {
            return Err(warning_default_error(
                "warning: there is no last warning identifier to target",
            ));
        };
        return set_mode_for_identifier(mode, &identifier);
    }

    if trimmed.eq_ignore_ascii_case("backtrace") || trimmed.eq_ignore_ascii_case("verbose") {
        return set_mode_for_special_mode(mode, trimmed);
    }

    let normalized = normalize_identifier(trimmed);
    set_mode_for_identifier(mode, &normalized)
}

fn set_mode_for_identifier(mode: WarningMode, identifier: &str) -> crate::BuiltinResult<Value> {
    with_manager(|mgr| {
        let previous = mgr.lookup_mode(identifier);
        let value = Value::Struct(mgr.state_struct_for(identifier, previous));
        mgr.set_identifier_mode(identifier, mode);
        Ok(value)
    })
}

fn reset_identifier_to_default(identifier: &str) -> crate::BuiltinResult<Value> {
    with_manager(|mgr| {
        let previous = mgr.lookup_mode(identifier);
        let value = Value::Struct(mgr.state_struct_for(identifier, previous));
        mgr.clear_identifier(identifier);
        Ok(value)
    })
}

fn set_mode_for_special_mode(mode: WarningMode, mode_name: &str) -> crate::BuiltinResult<Value> {
    let mode_lower = mode_name.trim().to_ascii_lowercase();
    if !matches!(mode, WarningMode::On | WarningMode::Off) {
        return Err(warning_default_error(format!(
            "warning: only 'on' or 'off' are valid states for '{mode_lower}'"
        )));
    }

    with_manager(|mgr| {
        let previous_enabled = if mode_lower == "backtrace" {
            let prev = mgr.backtrace_enabled;
            mgr.backtrace_enabled = matches!(mode, WarningMode::On);
            prev
        } else if mode_lower == "verbose" {
            let prev = mgr.verbose_enabled;
            mgr.verbose_enabled = matches!(mode, WarningMode::On);
            prev
        } else {
            return Err(warning_default_error(format!(
                "warning: unknown mode '{}'; expected 'backtrace' or 'verbose'",
                mode_name
            )));
        };

        let previous_state = if previous_enabled { "on" } else { "off" };
        let value = state_struct_value(&mode_lower, previous_state);
        Ok(value)
    })
}

fn default_command(rest: &[Value]) -> crate::BuiltinResult<Value> {
    match rest.len() {
        0 => {
            let snapshot = with_manager(|mgr| {
                let snapshot = mgr.snapshot();
                mgr.reset_defaults_only();
                snapshot
            });
            structs_to_cell(snapshot)
        }
        1 => {
            let identifier = value_to_string("warning", &rest[0])?;
            let trimmed = identifier.trim();
            if trimmed.eq_ignore_ascii_case("all") {
                let snapshot = with_manager(|mgr| {
                    let snapshot = mgr.snapshot();
                    mgr.reset_defaults_only();
                    snapshot
                });
                return structs_to_cell(snapshot);
            }
            if trimmed.eq_ignore_ascii_case("backtrace") {
                return with_manager(|mgr| {
                    let previous = if mgr.backtrace_enabled { "on" } else { "off" };
                    mgr.backtrace_enabled = false;
                    Ok(state_struct_value("backtrace", previous))
                });
            }
            if trimmed.eq_ignore_ascii_case("verbose") {
                return with_manager(|mgr| {
                    let previous = if mgr.verbose_enabled { "on" } else { "off" };
                    mgr.verbose_enabled = false;
                    Ok(state_struct_value("verbose", previous))
                });
            }
            if trimmed.eq_ignore_ascii_case("last") {
                let last_identifier = with_manager(|mgr| mgr.last_warning.clone());
                let Some((identifier, _)) = last_identifier else {
                    return Err(warning_default_error(
                        "warning: there is no last warning identifier to reset to default",
                    ));
                };
                return reset_identifier_to_default(&identifier);
            }
            let normalized = normalize_identifier(trimmed);
            reset_identifier_to_default(&normalized)
        }
        _ => Err(warning_default_error(
            "warning: 'default' accepts zero or one identifier argument",
        )),
    }
}

fn query_command(rest: &[Value]) -> crate::BuiltinResult<Value> {
    if rest.len() > 1 {
        return Err(warning_default_error(
            "warning: 'query' accepts at most one identifier argument",
        ));
    }

    let target = if rest.is_empty() {
        "all".to_string()
    } else {
        value_to_string("warning", &rest[0])?
    };

    with_manager(|mgr| {
        if target.trim().eq_ignore_ascii_case("all") {
            let snapshot = mgr.snapshot();
            let rows = snapshot.len();
            let entries: Vec<Value> = snapshot.into_iter().map(Value::Struct).collect();
            let cell = CellArray::new(entries, rows, 1).map_err(|e| {
                warning_default_error(format!("warning: failed to assemble query cell: {e}"))
            })?;
            Ok(Value::Cell(cell))
        } else if target.trim().eq_ignore_ascii_case("last") {
            if let Some((identifier, message)) = mgr.last_warning.clone() {
                let mut st = StructValue::new();
                st.fields
                    .insert("identifier".to_string(), Value::from(identifier));
                st.fields
                    .insert("message".to_string(), Value::from(message));
                st.fields.insert("state".to_string(), Value::from("last"));
                Ok(Value::Struct(st))
            } else {
                let mut st = StructValue::new();
                st.fields.insert("identifier".to_string(), Value::from(""));
                st.fields.insert("message".to_string(), Value::from(""));
                st.fields.insert("state".to_string(), Value::from("none"));
                Ok(Value::Struct(st))
            }
        } else if target.trim().eq_ignore_ascii_case("backtrace") {
            Ok(state_struct_value(
                "backtrace",
                if mgr.backtrace_enabled { "on" } else { "off" },
            ))
        } else if target.trim().eq_ignore_ascii_case("verbose") {
            Ok(state_struct_value(
                "verbose",
                if mgr.verbose_enabled { "on" } else { "off" },
            ))
        } else {
            let normalized = normalize_identifier(&target);
            let state = mgr.lookup_mode(&normalized);
            Ok(Value::Struct(mgr.state_struct_for(&normalized, state)))
        }
    })
}

fn status_command(rest: &[Value]) -> crate::BuiltinResult<Value> {
    if !rest.is_empty() {
        return Err(warning_default_error(
            "warning: 'status' does not accept additional arguments",
        ));
    }
    let value = query_command(&[])?;
    match &value {
        Value::Cell(cell) => {
            emit_stderr_line("Warning status:".to_string());
            for idx in 0..cell.data.len() {
                let entry = (*cell.data[idx]).clone();
                if let Value::Struct(st) = entry {
                    let identifier = st
                        .fields
                        .get("identifier")
                        .and_then(|v| value_to_string("warning", v).ok())
                        .unwrap_or_default();
                    let state = st
                        .fields
                        .get("state")
                        .and_then(|v| value_to_string("warning", v).ok())
                        .unwrap_or_default();
                    emit_stderr_line(format!("  {identifier}: {state}"));
                }
            }
        }
        Value::Struct(st) => {
            let identifier = st
                .fields
                .get("identifier")
                .and_then(|v| value_to_string("warning", v).ok())
                .unwrap_or_default();
            let state = st
                .fields
                .get("state")
                .and_then(|v| value_to_string("warning", v).ok())
                .unwrap_or_default();
            emit_stderr_line(format!("Warning status -> {identifier}: {state}"));
        }
        _ => {}
    }
    Ok(value)
}

fn backtrace_command(rest: &[Value]) -> crate::BuiltinResult<Value> {
    match rest.len() {
        0 => {
            let state = with_manager(|mgr| if mgr.backtrace_enabled { "on" } else { "off" });
            Ok(Value::from(state))
        }
        1 => {
            let setting = value_to_string("warning", &rest[0])?;
            match setting.trim().to_ascii_lowercase().as_str() {
                "on" => with_manager(|mgr| mgr.backtrace_enabled = true),
                "off" => with_manager(|mgr| mgr.backtrace_enabled = false),
                other => {
                    return Err(warning_default_error(format!(
                        "warning: backtrace mode must be 'on' or 'off', got '{other}'"
                    )))
                }
            }
            Ok(Value::Num(0.0))
        }
        _ => Err(warning_default_error(
            "warning: 'backtrace' accepts zero or one argument",
        )),
    }
}

fn handle_query_default() -> crate::BuiltinResult<Value> {
    query_command(&[])
}

fn apply_state_value(value: &Value) -> crate::BuiltinResult<()> {
    match value {
        Value::Struct(st) => apply_state_struct(st),
        Value::Cell(cell) => {
            for idx in 0..cell.data.len() {
                let entry = (*cell.data[idx]).clone();
                apply_state_value(&entry)?;
            }
            Ok(())
        }
        other => Err(warning_default_error(format!(
            "warning: expected a struct or cell array of structs, got {other:?}"
        ))),
    }
}

fn apply_state_struct(st: &StructValue) -> crate::BuiltinResult<()> {
    let identifier_value = st.fields.get("identifier").ok_or_else(|| {
        warning_default_error("warning: state struct must contain an 'identifier' field")
    })?;
    let state_value = st.fields.get("state").ok_or_else(|| {
        warning_default_error("warning: state struct must contain a 'state' field")
    })?;
    let identifier_raw = value_to_string("warning", identifier_value)?;
    let state_raw = value_to_string("warning", state_value)?;
    let identifier_trimmed = identifier_raw.trim();
    if identifier_trimmed.eq_ignore_ascii_case("all") {
        if let Some(mode) = parse_mode_keyword(&state_raw) {
            with_manager(|mgr| mgr.set_global_mode(mode));
        } else {
            return Err(warning_default_error(format!(
                "warning: unknown state '{}'",
                state_raw
            )));
        }
    } else if identifier_trimmed.eq_ignore_ascii_case("backtrace") {
        let state = state_raw.trim().to_ascii_lowercase();
        match state.as_str() {
            "on" => with_manager(|mgr| mgr.backtrace_enabled = true),
            "off" | "default" => with_manager(|mgr| mgr.backtrace_enabled = false),
            other => {
                return Err(warning_default_error(format!(
                    "warning: unknown backtrace state '{}'",
                    other
                )))
            }
        }
    } else if identifier_trimmed.eq_ignore_ascii_case("verbose") {
        let state = state_raw.trim().to_ascii_lowercase();
        match state.as_str() {
            "on" => with_manager(|mgr| mgr.verbose_enabled = true),
            "off" | "default" => with_manager(|mgr| mgr.verbose_enabled = false),
            other => {
                return Err(warning_default_error(format!(
                    "warning: unknown verbose state '{}'",
                    other
                )))
            }
        }
    } else if identifier_trimmed.eq_ignore_ascii_case("last") {
        let last_identifier = with_manager(|mgr| mgr.last_warning.clone());
        let Some((identifier, _)) = last_identifier else {
            return Err(warning_default_error(
                "warning: there is no last warning identifier to apply state",
            ));
        };
        if state_raw.trim().eq_ignore_ascii_case("default") {
            with_manager(|mgr| mgr.clear_identifier(&identifier));
        } else if let Some(mode) = parse_mode_keyword(&state_raw) {
            with_manager(|mgr| mgr.set_identifier_mode(&identifier, mode));
        } else {
            return Err(warning_default_error(format!(
                "warning: unknown state '{}'",
                state_raw
            )));
        }
    } else if state_raw.trim().eq_ignore_ascii_case("default") {
        let normalized = normalize_identifier(identifier_trimmed);
        with_manager(|mgr| mgr.clear_identifier(&normalized));
    } else if let Some(mode) = parse_mode_keyword(&state_raw) {
        let normalized = normalize_identifier(identifier_trimmed);
        with_manager(|mgr| mgr.set_identifier_mode(&normalized, mode));
    } else {
        return Err(warning_default_error(format!(
            "warning: unknown state '{}'",
            state_raw
        )));
    }
    Ok(())
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum WarningMode {
    On,
    Off,
    Once,
    Error,
}

impl WarningMode {
    fn keyword(self) -> &'static str {
        match self {
            WarningMode::On => "on",
            WarningMode::Off => "off",
            WarningMode::Once => "once",
            WarningMode::Error => "error",
        }
    }
}

#[derive(Clone, Copy)]
enum Command {
    SetMode(WarningMode),
    Default,
    Reset,
    Query,
    Status,
    Backtrace,
}

fn parse_command(text: &str) -> Option<Command> {
    match text.trim().to_ascii_lowercase().as_str() {
        "on" => Some(Command::SetMode(WarningMode::On)),
        "off" => Some(Command::SetMode(WarningMode::Off)),
        "once" => Some(Command::SetMode(WarningMode::Once)),
        "error" => Some(Command::SetMode(WarningMode::Error)),
        "default" => Some(Command::Default),
        "reset" => Some(Command::Reset),
        "query" => Some(Command::Query),
        "status" => Some(Command::Status),
        "backtrace" => Some(Command::Backtrace),
        _ => None,
    }
}

fn parse_mode_keyword(text: &str) -> Option<WarningMode> {
    match text.trim().to_ascii_lowercase().as_str() {
        "on" => Some(WarningMode::On),
        "off" => Some(WarningMode::Off),
        "once" => Some(WarningMode::Once),
        "error" => Some(WarningMode::Error),
        _ => None,
    }
}

#[derive(Clone, Copy)]
struct WarningRule {
    mode: WarningMode,
    triggered: bool,
}

impl WarningRule {
    fn new(mode: WarningMode) -> Self {
        Self {
            mode,
            triggered: false,
        }
    }
}

enum WarningAction {
    Suppress,
    Display,
    AsError,
}

struct WarningManager {
    default_mode: WarningMode,
    rules: HashMap<String, WarningRule>,
    once_seen_default: HashSet<String>,
    backtrace_enabled: bool,
    verbose_enabled: bool,
    last_warning: Option<(String, String)>,
}

impl Default for WarningManager {
    fn default() -> Self {
        Self {
            default_mode: WarningMode::On,
            rules: HashMap::new(),
            once_seen_default: HashSet::new(),
            backtrace_enabled: false,
            verbose_enabled: false,
            last_warning: None,
        }
    }
}

impl WarningManager {
    fn set_global_mode(&mut self, mode: WarningMode) {
        self.once_seen_default.clear();
        self.default_mode = mode;
    }

    fn set_identifier_mode(&mut self, identifier: &str, mode: WarningMode) {
        if mode == self.default_mode && !matches!(mode, WarningMode::Once) {
            self.rules.remove(identifier);
        } else {
            self.rules
                .insert(identifier.to_string(), WarningRule::new(mode));
        }
        if matches!(mode, WarningMode::Once) {
            self.once_seen_default.remove(identifier);
        }
    }

    fn clear_identifier(&mut self, identifier: &str) {
        self.rules.remove(identifier);
        self.once_seen_default.remove(identifier);
    }

    fn reset(&mut self) {
        self.default_mode = WarningMode::On;
        self.rules.clear();
        self.once_seen_default.clear();
        self.backtrace_enabled = false;
        self.verbose_enabled = false;
        self.last_warning = None;
    }

    fn reset_defaults_only(&mut self) {
        self.default_mode = WarningMode::On;
        self.once_seen_default.clear();
        self.rules.clear();
        self.backtrace_enabled = false;
        self.verbose_enabled = false;
    }

    fn action_for(&mut self, identifier: &str) -> WarningAction {
        if let Some(rule) = self.rules.get_mut(identifier) {
            return match rule.mode {
                WarningMode::On => WarningAction::Display,
                WarningMode::Off => WarningAction::Suppress,
                WarningMode::Error => WarningAction::AsError,
                WarningMode::Once => {
                    if rule.triggered {
                        WarningAction::Suppress
                    } else {
                        rule.triggered = true;
                        WarningAction::Display
                    }
                }
            };
        }

        match self.default_mode {
            WarningMode::On => WarningAction::Display,
            WarningMode::Off => WarningAction::Suppress,
            WarningMode::Error => WarningAction::AsError,
            WarningMode::Once => {
                if self.once_seen_default.contains(identifier) {
                    WarningAction::Suppress
                } else {
                    self.once_seen_default.insert(identifier.to_string());
                    WarningAction::Display
                }
            }
        }
    }

    fn record_last(&mut self, identifier: &str, message: &str) {
        self.last_warning = Some((identifier.to_string(), message.to_string()));
    }

    fn default_state_struct(&self) -> StructValue {
        let mut st = StructValue::new();
        st.fields
            .insert("identifier".to_string(), Value::from("all".to_string()));
        st.fields.insert(
            "state".to_string(),
            Value::from(self.default_mode.keyword()),
        );
        st
    }

    fn state_struct_for(&self, identifier: &str, rule: WarningRule) -> StructValue {
        let mut st = StructValue::new();
        st.fields.insert(
            "identifier".to_string(),
            Value::from(identifier.to_string()),
        );
        st.fields
            .insert("state".to_string(), Value::from(rule.mode.keyword()));
        st
    }

    fn lookup_mode(&self, identifier: &str) -> WarningRule {
        self.rules
            .get(identifier)
            .copied()
            .unwrap_or_else(|| WarningRule::new(self.default_mode))
    }

    fn snapshot(&self) -> Vec<StructValue> {
        let mut entries = Vec::new();
        entries.push(self.default_state_struct());
        for (id, rule) in self.rules.iter() {
            entries.push(self.state_struct_for(id, *rule));
        }
        entries.push(state_struct(
            "backtrace",
            if self.backtrace_enabled { "on" } else { "off" },
        ));
        entries.push(state_struct(
            "verbose",
            if self.verbose_enabled { "on" } else { "off" },
        ));
        entries
    }
}

fn value_to_string(context: &str, value: &Value) -> crate::BuiltinResult<String> {
    match value {
        Value::String(s) => Ok(s.clone()),
        Value::CharArray(ca) if ca.rows == 1 => Ok(ca.data.iter().collect()),
        Value::StringArray(sa) if sa.data.len() == 1 => Ok(sa.data[0].clone()),
        Value::CharArray(_) => Err(warning_default_error(format!(
            "{context}: expected scalar char array"
        ))),
        Value::StringArray(_) => Err(warning_default_error(format!(
            "{context}: expected scalar string"
        ))),
        other => String::try_from(other).map_err(|_| {
            warning_default_error(format!(
                "{context}: expected string-like argument, got {other:?}"
            ))
        }),
    }
}

fn is_message_identifier(text: &str) -> bool {
    let trimmed = text.trim();
    if trimmed.is_empty() || !trimmed.contains(':') {
        return false;
    }
    trimmed
        .chars()
        .all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, ':' | '_' | '.'))
}

fn normalize_identifier(raw: &str) -> String {
    let trimmed = raw.trim();
    if trimmed.is_empty() {
        DEFAULT_IDENTIFIER.to_string()
    } else if trimmed.contains(':') {
        trimmed.to_string()
    } else {
        format!("RunMat:{trimmed}")
    }
}

fn state_struct(identifier: &str, state: &str) -> StructValue {
    let mut st = StructValue::new();
    st.fields.insert(
        "identifier".to_string(),
        Value::from(identifier.to_string()),
    );
    st.fields
        .insert("state".to_string(), Value::from(state.to_string()));
    st
}

fn state_struct_value(identifier: &str, state: &str) -> Value {
    Value::Struct(state_struct(identifier, state))
}

fn structs_to_cell(structs: Vec<StructValue>) -> crate::BuiltinResult<Value> {
    let rows = structs.len();
    let values: Vec<Value> = structs.into_iter().map(Value::Struct).collect();
    CellArray::new(values, rows, 1)
        .map(Value::Cell)
        .map_err(|e| warning_default_error(format!("warning: failed to assemble state cell: {e}")))
}

#[cfg(test)]
pub(crate) mod tests {
    use super::*;
    use runmat_builtins::{ResolveContext, Type};

    static TEST_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));

    fn unwrap_error(err: crate::RuntimeError) -> crate::RuntimeError {
        err
    }

    fn reset_manager() {
        with_manager(WarningManager::reset);
    }

    fn assert_state_struct(value: &Value, identifier: &str, state: &str) {
        match value {
            Value::Struct(st) => {
                let id = st
                    .fields
                    .get("identifier")
                    .and_then(|v| String::try_from(v).ok())
                    .unwrap_or_default();
                let st_state = st
                    .fields
                    .get("state")
                    .and_then(|v| String::try_from(v).ok())
                    .unwrap_or_default();
                assert_eq!(id, identifier);
                assert_eq!(st_state, state);
            }
            other => panic!("expected state struct, got {other:?}"),
        }
    }

    fn structs_from_value(value: Value) -> Vec<StructValue> {
        match value {
            Value::Cell(cell) => cell
                .data
                .iter()
                .map(|ptr| unsafe { &*ptr.as_raw() }.clone())
                .map(|value| match value {
                    Value::Struct(st) => st,
                    other => panic!("expected struct entry, got {other:?}"),
                })
                .collect(),
            Value::Struct(st) => vec![st],
            other => panic!("expected struct array, got {other:?}"),
        }
    }

    fn field_str(struct_value: &StructValue, field: &str) -> Option<String> {
        struct_value
            .fields
            .get(field)
            .and_then(|value| String::try_from(value).ok())
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn emits_basic_warning() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let result = warning_builtin(vec![Value::from("Hello world!")]).expect("warning ok");
        assert!(matches!(result, Value::Num(_)));
        let last = with_manager(|mgr| mgr.last_warning.clone());
        assert_eq!(
            last,
            Some((DEFAULT_IDENTIFIER.to_string(), "Hello world!".to_string()))
        );
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn emits_warning_with_identifier_and_format() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let args = vec![
            Value::from("runmat:demo:test"),
            Value::from("value is %d"),
            Value::Int(runmat_builtins::IntValue::I32(7)),
        ];
        warning_builtin(args).expect("warning ok");
        let last = with_manager(|mgr| mgr.last_warning.clone());
        assert_eq!(
            last,
            Some(("runmat:demo:test".to_string(), "value is 7".to_string()))
        );
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn off_suppresses_warning() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let state =
            warning_builtin(vec![Value::from("off"), Value::from("all")]).expect("state change");
        assert_state_struct(&state, "all", "on");
        warning_builtin(vec![Value::from("Should suppress")]).expect("warning ok");
        let last = with_manager(|mgr| mgr.last_warning.clone());
        assert!(last.is_none());
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn once_only_emits_first_warning() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let state =
            warning_builtin(vec![Value::from("once"), Value::from("all")]).expect("state change");
        assert_state_struct(&state, "all", "on");
        warning_builtin(vec![Value::from("First")]).expect("warning ok");
        warning_builtin(vec![Value::from("Second")]).expect("warning ok");
        let last = with_manager(|mgr| mgr.last_warning.clone());
        assert_eq!(
            last,
            Some((DEFAULT_IDENTIFIER.to_string(), "First".to_string()))
        );
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn error_mode_promotes_to_error() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let previous =
            warning_builtin(vec![Value::from("error"), Value::from("all")]).expect("state change");
        assert_state_struct(&previous, "all", "on");
        let err =
            unwrap_error(warning_builtin(vec![Value::from("Promoted")]).expect_err("should error"));
        assert_eq!(err.identifier(), Some(DEFAULT_IDENTIFIER));
        assert_eq!(err.message(), "Promoted");
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn query_returns_state_struct() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        warning_builtin(vec![Value::from("off"), Value::from("runmat:demo:test")])
            .expect("state change");
        let value = warning_builtin(vec![Value::from("query"), Value::from("runmat:demo:test")])
            .expect("query ok");
        match value {
            Value::Struct(st) => {
                let state = st.fields.get("state").unwrap();
                assert_eq!(String::try_from(state).unwrap(), "off".to_string());
            }
            other => panic!("expected struct, got {other:?}"),
        }
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn state_struct_restores_mode() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let snapshot =
            warning_builtin(vec![Value::from("query"), Value::from("all")]).expect("query all");
        warning_builtin(vec![Value::from("off"), Value::from("all")]).expect("off all");
        warning_builtin(vec![snapshot]).expect("restore");
        let state = warning_builtin(vec![
            Value::from("query"),
            Value::from("runmat:demo:restored"),
        ])
        .expect("query")
        .expect_struct();
        assert_eq!(
            String::try_from(state.fields.get("state").unwrap()).unwrap(),
            "on".to_string()
        );
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn set_mode_backtrace_via_state() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let prev = warning_builtin(vec![Value::from("on"), Value::from("backtrace")])
            .expect("enable backtrace");
        assert_state_struct(&prev, "backtrace", "off");
        assert!(with_manager(|mgr| mgr.backtrace_enabled));
        let prev = warning_builtin(vec![Value::from("off"), Value::from("backtrace")])
            .expect("disable backtrace");
        assert_state_struct(&prev, "backtrace", "on");
        assert!(!with_manager(|mgr| mgr.backtrace_enabled));
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn set_mode_verbose_via_state() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let prev = warning_builtin(vec![Value::from("on"), Value::from("verbose")])
            .expect("enable verbose");
        assert_state_struct(&prev, "verbose", "off");
        assert!(with_manager(|mgr| mgr.verbose_enabled));
        let prev = warning_builtin(vec![Value::from("off"), Value::from("verbose")])
            .expect("disable verbose");
        assert_state_struct(&prev, "verbose", "on");
        assert!(!with_manager(|mgr| mgr.verbose_enabled));
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn special_mode_rejects_invalid_state() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let err = unwrap_error(
            warning_builtin(vec![Value::from("once"), Value::from("backtrace")])
                .expect_err("invalid state"),
        );
        assert_eq!(err.identifier(), Some(DEFAULT_IDENTIFIER));
        assert!(
            err.message().contains("only 'on' or 'off'"),
            "unexpected error message: {}",
            err.message()
        );
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn set_mode_last_requires_identifier() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let err = unwrap_error(
            warning_builtin(vec![Value::from("off"), Value::from("last")])
                .expect_err("missing last"),
        );
        assert_eq!(err.identifier(), Some(DEFAULT_IDENTIFIER));
        assert!(
            err.message().contains("no last warning identifier"),
            "unexpected error: {}",
            err.message()
        );
        warning_builtin(vec![Value::from("Hello!")]).expect("emit warning");
        let previous =
            warning_builtin(vec![Value::from("off"), Value::from("last")]).expect("disable last");
        assert_state_struct(&previous, DEFAULT_IDENTIFIER, "on");
        let last_mode = with_manager(|mgr| mgr.lookup_mode(DEFAULT_IDENTIFIER).mode);
        assert!(matches!(last_mode, WarningMode::Off));
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn default_returns_snapshot() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        warning_builtin(vec![
            Value::from("off"),
            Value::from("runmat:demo:snapshot"),
        ])
        .expect("state change");
        let snapshot = warning_builtin(vec![Value::from("default")]).expect("default");
        let structs = structs_from_value(snapshot);
        assert!(structs.iter().any(|st| {
            field_str(st, "identifier").as_deref() == Some("all")
                && field_str(st, "state").as_deref() == Some("on")
        }));
        assert!(structs.iter().any(|st| {
            field_str(st, "identifier").as_deref() == Some("runmat:demo:snapshot")
                && field_str(st, "state").as_deref() == Some("off")
        }));
        assert!(structs
            .iter()
            .any(|st| { field_str(st, "identifier").as_deref() == Some("backtrace") }));
        assert!(structs
            .iter()
            .any(|st| { field_str(st, "identifier").as_deref() == Some("verbose") }));
        assert!(with_manager(|mgr| mgr.rules.is_empty()));
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn default_special_modes_reset() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        warning_builtin(vec![Value::from("on"), Value::from("verbose")]).expect("enable verbose");
        warning_builtin(vec![Value::from("on"), Value::from("backtrace")])
            .expect("enable backtrace");
        let verbose_prev =
            warning_builtin(vec![Value::from("default"), Value::from("verbose")]).expect("default");
        assert_state_struct(&verbose_prev, "verbose", "on");
        assert!(!with_manager(|mgr| mgr.verbose_enabled));
        let backtrace_prev =
            warning_builtin(vec![Value::from("default"), Value::from("backtrace")])
                .expect("default");
        assert_state_struct(&backtrace_prev, "backtrace", "on");
        assert!(!with_manager(|mgr| mgr.backtrace_enabled));
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn query_backtrace_and_verbose() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        warning_builtin(vec![Value::from("on"), Value::from("verbose")]).expect("enable verbose");
        let verbose = warning_builtin(vec![Value::from("query"), Value::from("verbose")])
            .expect("query verbose");
        assert_state_struct(&verbose, "verbose", "on");
        let backtrace = warning_builtin(vec![Value::from("query"), Value::from("backtrace")])
            .expect("query backtrace");
        assert_state_struct(&backtrace, "backtrace", "off");
    }

    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
    #[test]
    fn apply_state_struct_special_modes() {
        let _guard = TEST_LOCK.lock().unwrap();
        reset_manager();
        let mut backtrace = StructValue::new();
        backtrace
            .fields
            .insert("identifier".to_string(), Value::from("backtrace"));
        backtrace
            .fields
            .insert("state".to_string(), Value::from("on"));
        warning_builtin(vec![Value::Struct(backtrace)]).expect("apply backtrace");
        assert!(with_manager(|mgr| mgr.backtrace_enabled));

        let mut verbose = StructValue::new();
        verbose
            .fields
            .insert("identifier".to_string(), Value::from("verbose"));
        verbose
            .fields
            .insert("state".to_string(), Value::from("default"));
        warning_builtin(vec![Value::Struct(verbose)]).expect("apply verbose");
        assert!(!with_manager(|mgr| mgr.verbose_enabled));
    }

    #[test]
    fn warning_type_is_unknown() {
        assert_eq!(
            warning_type(&[Type::String], &ResolveContext::new(Vec::new())),
            Type::Unknown
        );
    }

    trait ExpectStruct {
        fn expect_struct(self) -> StructValue;
    }

    impl ExpectStruct for Value {
        fn expect_struct(self) -> StructValue {
            match self {
                Value::Struct(st) => st,
                other => panic!("expected struct, got {other:?}"),
            }
        }
    }
}
