//! Error types for the orbital-core crate.
//!
//! A single `Error` enum covers all failure modes: TLE parsing, propagation
//! divergence, checksum validation, and free category composition.  Each
//! variant wraps enough context for a meaningful error message.

use crate::tle::newtypes::LineNumber;

/// All errors in the orbital-core crate.
#[derive(Debug)]
pub enum Error {
    TleParseFailed(TleParseDetail),
    InvalidChecksum {
        expected: u8,
        actual: u8,
    },
    InvalidLineFormat {
        line_number: LineNumber,
        reason: &'static str,
    },
    PropagationDiverged {
        context: &'static str,
    },
    FreeCategoryError(comp_cat_rs::collapse::free_category::FreeCategoryError),
}

/// Detail for TLE parse failures.
#[derive(Debug)]
pub enum TleParseDetail {
    MissingField(&'static str),
    MalformedFloat {
        field: &'static str,
        raw: String,
    },
    MalformedInt {
        field: &'static str,
        raw: String,
    },
    LineLengthWrong {
        expected: usize,
        actual: usize,
    },
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::TleParseFailed(detail) => write!(f, "TLE parse failed: {detail}"),
            Self::InvalidChecksum { expected, actual } => {
                write!(f, "invalid checksum: expected {expected}, got {actual}")
            }
            Self::InvalidLineFormat {
                line_number,
                reason,
            } => write!(f, "invalid line {} format: {reason}", line_number.value()),
            Self::PropagationDiverged { context } => {
                write!(f, "propagation diverged: {context}")
            }
            Self::FreeCategoryError(e) => write!(f, "free category error: {e:?}"),
        }
    }
}

impl std::fmt::Display for TleParseDetail {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::MissingField(name) => write!(f, "missing field: {name}"),
            Self::MalformedFloat { field, raw } => {
                write!(f, "malformed float in {field}: {raw:?}")
            }
            Self::MalformedInt { field, raw } => {
                write!(f, "malformed int in {field}: {raw:?}")
            }
            Self::LineLengthWrong { expected, actual } => {
                write!(f, "line length {actual}, expected {expected}")
            }
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<comp_cat_rs::collapse::free_category::FreeCategoryError> for Error {
    fn from(e: comp_cat_rs::collapse::free_category::FreeCategoryError) -> Self {
        Self::FreeCategoryError(e)
    }
}

impl From<TleParseDetail> for Error {
    fn from(detail: TleParseDetail) -> Self {
        Self::TleParseFailed(detail)
    }
}
