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

/// Top-level error returned from all public Brittle API methods.
#[derive(Debug, Error)]
pub enum BrittleError {
    #[error("{0}")]
    Store(#[from] StoreError),

    #[error("{0}")]
    Validation(#[from] ValidationError),

    #[error("{0}")]
    BibTeX(#[from] BibtexError),
}

/// Errors from the storage layer.
#[derive(Debug, Error)]
pub enum StoreError {
    #[error("{entity_type} not found: {id}")]
    NotFound { entity_type: EntityType, id: String },

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

    #[error("serialization error: {message}")]
    Serialization { message: String },

    #[error("deserialization error for {path}: {message}")]
    Deserialization { path: PathBuf, message: String },

    #[error("git error: {0}")]
    Git(#[from] git2::Error),

    #[error("repository not found at {path}")]
    RepoNotFound { path: PathBuf },

    #[error("repository already exists at {path}")]
    RepoAlreadyExists { path: PathBuf },
}

/// The kind of entity involved in a not-found error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityType {
    Reference,
    Library,
    Annotation,
    Snapshot,
}

impl std::fmt::Display for EntityType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            EntityType::Reference => write!(f, "Reference"),
            EntityType::Library => write!(f, "Library"),
            EntityType::Annotation => write!(f, "Annotation"),
            EntityType::Snapshot => write!(f, "Snapshot"),
        }
    }
}

/// Business logic validation errors.
#[derive(Debug, Error)]
pub enum ValidationError {
    #[error(
        "library cycle detected: moving library {library_id} under {parent_id} would create a cycle"
    )]
    LibraryCycle {
        library_id: String,
        parent_id: String,
    },

    #[error("library {id} has children and cannot be deleted; delete or move children first")]
    LibraryHasChildren { id: String },

    #[error("cite key already exists: {cite_key}")]
    DuplicateCiteKey { cite_key: String },

    #[error("cite key cannot be empty")]
    EmptyCiteKey,

    #[error("library name cannot be empty")]
    EmptyLibraryName,

    #[error("reference {reference_id} has no PDF attached")]
    NoPdfAttached { reference_id: String },

    #[error("PDF file not found: {path}")]
    PdfNotFound { path: PathBuf },

    #[error("there are uncommitted changes; create a snapshot or call discard_changes() first")]
    UncommittedChanges,
}

/// Errors specific to BibTeX export.
#[derive(Debug, Error)]
pub enum BibtexError {
    #[error("reference '{cite_key}' ({entry_type}): missing required field '{field}'")]
    MissingRequiredField {
        cite_key: String,
        entry_type: String,
        field: String,
    },
}
