//! Error types for the Robomotion runtime.

use serde::{Deserialize, Serialize};
use thiserror::Error;

/// Result type alias for Robomotion operations.
pub type Result<T> = std::result::Result<T, RobomotionError>;

/// Error type for Robomotion operations.
#[derive(Error, Debug)]
pub enum RobomotionError {
    #[error("Runtime not initialized")]
    NotInitialized,

    #[error("Node handler not found: {0}")]
    HandlerNotFound(String),

    #[error("Factory not found: {0}")]
    FactoryNotFound(String),

    #[error("Variable error: {0}")]
    Variable(String),

    #[error("Serialization error: {0}")]
    Serialization(#[from] serde_json::Error),

    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("gRPC error: {0}")]
    Grpc(#[from] tonic::Status),

    #[error("gRPC transport error: {0}")]
    Transport(#[from] tonic::transport::Error),

    #[error("OAuth error: {0}")]
    OAuth(String),

    #[error("LMO error: {0}")]
    Lmo(String),

    #[error("Compression error: {0}")]
    Compression(String),

    #[error("{code}: {message}")]
    Custom { code: String, message: String },
}

/// Structured error that can be serialized as JSON.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StructuredError {
    pub code: String,
    pub message: String,
}

impl StructuredError {
    /// Create a new structured error.
    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
        Self {
            code: code.into(),
            message: message.into(),
        }
    }
}

impl std::fmt::Display for StructuredError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match serde_json::to_string(self) {
            Ok(json) => write!(f, "{}", json),
            Err(_) => write!(f, "{}: {}", self.code, self.message),
        }
    }
}

impl std::error::Error for StructuredError {}

impl From<StructuredError> for RobomotionError {
    fn from(err: StructuredError) -> Self {
        RobomotionError::Custom {
            code: err.code,
            message: err.message,
        }
    }
}
