use crate::diag::{Diag, DiagCode};
use crate::ir::dtype;
use crate::parse::ast::Span;

// ── Constructor helpers ──────────────────────────────────────────────────────

pub fn parse_int_imaginary(value: i64, span: Span) -> Diag {
    Diag::new(
        DiagCode::ParseIntImaginary,
        format!(
            "integer imaginary literal `{value}j` is not supported; \
             use float form `{value}.0j` instead"
        ),
        Some((span.start, span.end)),
        None,
    )
}

pub fn parse_undefined_var(name: &str, span: Span) -> Diag {
    Diag::new(
        DiagCode::ParseUndefinedVar,
        format!("undefined variable `{name}`"),
        Some((span.start, span.end)),
        None,
    )
}

pub fn parse_unknown_dtype(name: &str, span: Span) -> Diag {
    let expected = dtype::all_dtype_names().join(", ");
    Diag::new(
        DiagCode::ParseUnknownDType,
        format!("unknown type `{name}`"),
        Some((span.start, span.end)),
        Some(format!("expected one of: {expected}")),
    )
}

pub fn parse_syntax(msg: &str, span: Span) -> Diag {
    Diag::new(
        DiagCode::ParseSyntax,
        format!("syntax error: {msg}"),
        Some((span.start, span.end)),
        None,
    )
}

pub fn parse_reserved_identifier(name: &str, span: Span) -> Diag {
    Diag::new(
        DiagCode::ParseReservedIdentifier,
        format!("reserved word `{name}` cannot be used as an identifier"),
        Some((span.start, span.end)),
        Some("use a different name for the variable or parameter".to_string()),
    )
}

#[cfg(test)]
mod tests {
    use super::parse_int_imaginary;
    use crate::diag::Severity;
    use crate::parse::ast::Span;

    #[test]
    fn parse_error_report_contains_message() {
        let err = parse_int_imaginary(2, Span { start: 4, end: 6 });
        let report = err.report_string(
            "|x: f64|\nx = 2j\nx",
            "|x: f64|\nx = 2j\nx",
            Severity::Error,
        );
        assert!(report.contains("integer imaginary literal `2j`"));
    }
}
