//! Structured error types for PixelFlow core and downstream crates.

use std::error::Error;
use std::fmt;

/// Convenient result alias for PixelFlow operations.
pub type Result<T> = std::result::Result<T, PixelFlowError>;

/// Stable high-level category for routing errors across APIs and plugins.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ErrorCategory {
    /// Failure in core graph, render, scheduler, cache, frame, or metadata logic.
    Core,
    /// Failure while constructing or validating graph topology.
    Graph,
    /// Failure produced by script parsing, evaluation, or script-to-graph binding.
    Script,
    /// Failure produced by plugin loading, registration, or execution.
    Plugin,
    /// Failure produced by source indexing, fingerprinting, or decoding.
    Source,
    /// Failure caused by unsupported or inconsistent pixel format information.
    Format,
    /// Failure while reading from or writing to an external resource.
    Io,
    /// Internal invariant violation that should be reported as a bug.
    Internal,
}

impl ErrorCategory {
    /// Returns a stable lowercase category name for diagnostics and ABI mapping.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Core => "core",
            Self::Graph => "graph",
            Self::Script => "script",
            Self::Plugin => "plugin",
            Self::Source => "source",
            Self::Format => "format",
            Self::Io => "io",
            Self::Internal => "internal",
        }
    }
}

impl fmt::Display for ErrorCategory {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.as_str())
    }
}

/// Stable machine-readable error code within an [`ErrorCategory`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ErrorCode(&'static str);

impl ErrorCode {
    /// Creates an error code from a static dotted identifier.
    #[must_use]
    pub const fn new(code: &'static str) -> Self {
        Self(code)
    }

    /// Returns the stable code string.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        self.0
    }
}

impl fmt::Display for ErrorCode {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.0)
    }
}

/// Structured error value used by public PixelFlow APIs.
#[derive(Debug)]
pub struct PixelFlowError {
    category: ErrorCategory,
    code: ErrorCode,
    message: String,
}

impl PixelFlowError {
    /// Creates a structured error with stable category/code and human message.
    #[must_use]
    pub fn new(category: ErrorCategory, code: ErrorCode, message: impl Into<String>) -> Self {
        Self {
            category,
            code,
            message: message.into(),
        }
    }

    /// Returns high-level error category.
    #[must_use]
    pub const fn category(&self) -> ErrorCategory {
        self.category
    }

    /// Returns machine-readable error code.
    #[must_use]
    pub const fn code(&self) -> ErrorCode {
        self.code
    }

    /// Returns human-readable diagnostic message.
    #[must_use]
    pub fn message(&self) -> &str {
        &self.message
    }
}

impl fmt::Display for PixelFlowError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            formatter,
            "{} error {}: {}",
            self.category, self.code, self.message
        )
    }
}

impl Error for PixelFlowError {}
