use std::io;
use std::path::PathBuf;
use thiserror::Error;

/// Result type for WAL operations.
pub type Result<T> = std::result::Result<T, QuarantineError>;

/// Errors that can occur during WAL operations.
#[derive(Error, Debug)]
pub enum QuarantineError {
    /// IO error at a specific path.
    #[error("IO error at {path:?}: {source}")]
    Io {
        /// The path where the error occurred.
        path: PathBuf,
        /// The underlying IO error.
        source: io::Error,
    },

    /// Failed to fsync a file.
    #[error("Failed to fsync {path:?}: {source}")]
    FsyncFailed {
        /// The path of the file.
        path: PathBuf,
        /// The underlying IO error.
        source: io::Error,
    },

    /// File is locked by another process.
    #[error("File is locked: {path:?}")]
    FileLocked {
        /// The path of the locked file.
        path: PathBuf,
    },

    /// CRC32C checksum mismatch (fast integrity check, detects torn writes).
    #[error(
        "CRC32C mismatch at offset {offset}: expected {expected:#010x}, actual {actual:#010x}"
    )]
    Crc32cMismatch {
        /// File offset where the corrupt record starts.
        offset: u64,
        /// Expected CRC32C value from the record header.
        expected: u32,
        /// Actual CRC32C computed from the data.
        actual: u32,
    },

    /// Record length field is invalid (zero or exceeds MAX_RECORD_SIZE).
    #[error("Invalid record length at offset {offset}: {length} bytes")]
    InvalidRecordLength {
        /// File offset where the record starts.
        offset: u64,
        /// The invalid length value read.
        length: u32,
    },

    /// Generic record corruption with a descriptive reason.
    #[error("Corrupt record at offset {offset}: {reason}")]
    CorruptRecord {
        /// File offset where corruption was detected.
        offset: u64,
        /// Human-readable description of the corruption.
        reason: String,
    },

    /// Generic IO error.
    #[error(transparent)]
    IoGeneric(#[from] io::Error),
}

impl QuarantineError {
    /// Helper to create an `Io` error variant.
    pub fn io(path: impl Into<PathBuf>, source: io::Error) -> Self {
        Self::Io { path: path.into(), source }
    }
}
