// Copyright 2025-2026 LabOverWire. All rights reserved.
// SPDX-License-Identifier: AGPL-3.0-only

use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
    #[cfg(feature = "fjall-backend")]
    #[error("storage error: {0}")]
    Storage(#[from] fjall::Error),

    #[error("storage error: {0}")]
    StorageGeneric(String),

    #[error("entity not found: {entity} id={id}")]
    NotFound { entity: String, id: String },

    #[error("serialization error: {0}")]
    Serialization(#[from] serde_json::Error),

    #[error("invalid key format: {0}")]
    InvalidKey(String),

    #[error("validation error: {0}")]
    Validation(String),

    #[error("constraint violation: {0}")]
    ConstraintViolation(String),

    #[error("internal error: {0}")]
    Internal(String),

    #[error("system time error: {0}")]
    SystemTime(String),

    #[error("data corruption detected: {entity}/{id} - checksum mismatch")]
    Corruption { entity: String, id: String },

    #[error("backup/restore failed: {0}")]
    BackupFailed(String),

    #[error("concurrent modification conflict: {0}")]
    Conflict(String),

    #[error("schema validation failed: {entity}.{field} - {reason}")]
    SchemaViolation {
        entity: String,
        field: String,
        reason: String,
    },

    #[error("unique constraint violation: {entity}.{field} value '{value}' already exists")]
    UniqueViolation {
        entity: String,
        field: String,
        value: String,
    },

    #[error(
        "foreign key violation: {entity}.{field} references non-existent {target_entity}/{target_id}"
    )]
    ForeignKeyViolation {
        entity: String,
        field: String,
        target_entity: String,
        target_id: String,
    },

    #[error(
        "foreign key restrict: cannot delete {entity}/{id} - referenced by {referencing_entity}"
    )]
    ForeignKeyRestrict {
        entity: String,
        id: String,
        referencing_entity: String,
    },

    #[error("not null constraint violation: {entity}.{field} cannot be null")]
    NotNullViolation { entity: String, field: String },

    #[error("invalid foreign key value type")]
    InvalidForeignKey,

    #[error("forbidden: {0}")]
    Forbidden(String),

    #[error("cascade blocked: cannot delete {0} - cross-owned entity has non-nullable FK field")]
    CascadeBlocked(Box<CascadeBlockedInfo>),
}

#[derive(Debug)]
pub struct CascadeBlockedInfo {
    pub entity: String,
    pub id: String,
    pub blocked_entity: String,
    pub blocked_id: String,
    pub blocked_field: String,
    pub blocked_owner: String,
}

impl std::fmt::Display for CascadeBlockedInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}/{} by {}/{} (field '{}', owner '{}')",
            self.entity,
            self.id,
            self.blocked_entity,
            self.blocked_id,
            self.blocked_field,
            self.blocked_owner
        )
    }
}

pub type Result<T> = std::result::Result<T, Error>;
