use alloc::{string::String, vec::Vec};

use crate::span::Span;

pub type Result<T> = core::result::Result<T, Diagnostic>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
    pub kind: DiagnosticKind,
    pub span: Span,
    pub message: String,
    pub labels: Vec<DiagnosticLabel>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiagnosticLabel {
    pub span: Span,
    pub message: String,
}

impl Diagnostic {
    pub fn new(kind: DiagnosticKind, span: Span, message: impl Into<String>) -> Self {
        Self {
            kind,
            span,
            message: message.into(),
            labels: Vec::new(),
        }
    }

    pub fn syntax(span: Span, message: impl Into<String>) -> Self {
        Self::new(DiagnosticKind::Syntax, span, message)
    }

    pub fn with_label(mut self, span: Span, message: impl Into<String>) -> Self {
        self.labels.push(DiagnosticLabel {
            span,
            message: message.into(),
        });
        self
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagnosticKind {
    Syntax,
    UnresolvedIdentifier,
    TypeMismatch,
    ConstraintViolation,
    Conflict,
    DefaultConflict,
    Cycle,
    Import,
    MatchFailure,
    Materialize,
    UnsupportedFeature,
}
