use std::fmt;
use std::io;

/// Custom error type for tegdb operations
#[derive(Debug)]
pub enum Error {
    /// I/O error from underlying file operations
    Io(io::Error),
    /// Error when key is too large (> 1KB)
    KeyTooLarge(usize),
    /// Error when value is too large (> 256KB)
    ValueTooLarge(usize),
    /// Error when database file is locked by another process
    FileLocked(String),
    /// Error when file is corrupted
    Corrupted(String),
    /// Storage file does not have the expected magic header
    InvalidMagic,
    /// Storage file format version is unsupported
    UnsupportedVersion(u16),
    /// Storage file header is malformed
    CorruptHeader(&'static str),
    /// SQL parsing, planning, or execution error
    SqlError(String),
    /// Error during SQL parsing
    ParseError(String),
    /// Error during query planning
    PlanError(String),
    /// Table not found
    TableNotFound(String),
    /// Column not found
    ColumnNotFound(String),
    /// Exceeded configured in-memory quota (too many keys)
    OutOfMemoryQuota { max_keys: usize },
    /// Exceeded configured on-disk quota (log file would exceed limit)
    OutOfStorageQuota { bytes: u64 },
    /// Other database errors
    Other(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Io(err) => write!(f, "I/O error: {err}"),
            Error::KeyTooLarge(size) => write!(f, "Key too large: {size} bytes (max 1KB)"),
            Error::ValueTooLarge(size) => write!(f, "Value too large: {size} bytes (max 256KB)"),
            Error::FileLocked(msg) => write!(f, "Database file is locked: {msg}"),
            Error::Corrupted(msg) => write!(f, "Database corrupted: {msg}"),
            Error::InvalidMagic => write!(f, "Invalid storage file magic header"),
            Error::UnsupportedVersion(v) => write!(f, "Unsupported storage file version: {v}"),
            Error::CorruptHeader(msg) => write!(f, "Corrupt storage header: {msg}"),
            Error::SqlError(msg) => write!(f, "SQL error: {msg}"),
            Error::ParseError(msg) => write!(f, "SQL parse error: {msg}"),
            Error::PlanError(msg) => write!(f, "Query planning error: {msg}"),
            Error::TableNotFound(table) => write!(f, "Table '{table}' not found"),
            Error::ColumnNotFound(column) => write!(f, "Column '{column}' not found"),
            Error::OutOfMemoryQuota { max_keys } => {
                write!(f, "In-memory quota exceeded (max {max_keys} keys)")
            }
            Error::OutOfStorageQuota { bytes } => {
                write!(f, "Storage quota exceeded (max {bytes} bytes)")
            }
            Error::Other(msg) => write!(f, "Database error: {msg}"),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::Io(err) => Some(err),
            _ => None,
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::Io(err)
    }
}

impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Self {
        Error::Other(format!("JSON parsing error: {}", err))
    }
}

/// Result type for tegdb operations
pub type Result<T> = std::result::Result<T, Error>;
