//! Pure-typing errors for `cortex-core`.
//!
//! These errors describe **shape** and **typing** failures: an ID string that
//! cannot be parsed, a serialized record whose `schema_version` does not match
//! what this build understands, a JSON value that fails structural validation.
//!
//! `cortex-core` performs **no I/O** (per BUILD_SPEC §8). Surface I/O failures
//! at the crate boundary that owns them (`cortex-ledger`, `cortex-store`, …)
//! by composing them with `CoreError` via the usual `From` wiring; do **not**
//! add I/O variants here.

use thiserror::Error;

/// Errors raised by `cortex-core` parsing and validation.
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum CoreError {
    /// A string failed to parse as a prefix-ULID identifier.
    ///
    /// Either the prefix did not match the expected variant (e.g. `trc_…`
    /// passed where `evt_…` was required) or the ULID body was malformed.
    #[error("id parse error: {0}")]
    IdParse(String),

    /// A persisted record was decoded with an unexpected `schema_version`.
    ///
    /// Producers MUST refuse to operate on records whose schema is newer than
    /// they understand; consumers MAY upcast older records via documented
    /// migrations. See [`crate::SCHEMA_VERSION`].
    #[error("schema version mismatch: found {found}, expected {expected}")]
    SchemaMismatch {
        /// The version observed on the wire / in storage.
        found: u16,
        /// The version this build of `cortex-core` understands.
        expected: u16,
    },

    /// A JSON value failed structural validation (missing field, wrong type,
    /// constraint violation). The contained string is operator-facing detail.
    #[error("json validation failed: {0}")]
    JsonValidation(String),

    /// Generic value-shape validation failure (non-JSON contexts: invariants,
    /// pre-conditions on typed values).
    ///
    /// Prefer [`CoreError::JsonValidation`] for failures that originated in a
    /// JSON decode / schema check.
    #[error("validation failed: {0}")]
    Validation(String),
}

/// Crate-wide `Result` alias.
pub type CoreResult<T> = Result<T, CoreError>;

/// Backwards-compat alias for the pre-Lane-1.A error name.
///
/// Other crates in the workspace still reference `cortex_core::CortexError`;
/// keep this alias available until they are migrated to [`CoreError`] in a
/// follow-up lane. **New code MUST use [`CoreError`] directly.** When all
/// downstream call sites have moved over, drop this alias in a single PR
/// alongside a `SCHEMA_VERSION` non-bump (this is an API rename, not a wire
/// rename).
pub use CoreError as CortexError;

/// Backwards-compat alias for the pre-Lane-1.A `Result` alias.
///
/// See the migration note on [`CortexError`]. **New code MUST use
/// [`CoreResult`] directly.**
pub type CortexResult<T> = CoreResult<T>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn schema_mismatch_renders_clean_message() {
        let e = CoreError::SchemaMismatch {
            found: 2,
            expected: 1,
        };
        assert_eq!(
            e.to_string(),
            "schema version mismatch: found 2, expected 1"
        );
    }

    #[test]
    fn id_parse_renders_clean_message() {
        let e = CoreError::IdParse("bad".into());
        assert_eq!(e.to_string(), "id parse error: bad");
    }
}
