use hex::FromHexError;
use miette::Diagnostic;
use recipher::errors::RecipherError;
use static_assertions::assert_impl_all;
use thiserror::Error;
use zerokms_protocol::{ViturRequestError, ViturRequestErrorKind};

#[derive(Diagnostic, Error, Debug)]
pub enum CreateKeysetError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

impl CreateKeysetError {
    pub fn is_conflict(&self) -> bool {
        match self {
            CreateKeysetError::RequestFailed(e) => e.is_conflict(),
        }
    }
}

#[derive(Diagnostic, Error, Debug)]
pub enum GrantKeysetError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum RevokeKeysetError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum ListKeysetError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum EnableKeysetError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum DisableKeysetError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum ModifyKeysetError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum CreateClientError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum ListClientError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum RevokeClientError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum RetrieveKeyError {
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
    #[error("Received an invalid number of keys from request. Expected {expected} but received {received}")]
    InvalidNumberOfKeys { expected: usize, received: usize },

    /// Represents an error that occurs when a single key retrieval fails.
    /// May be part of a batch retrieval operation.
    #[error("Failed to retrieve key: {0}")]
    FailedRetrieval(String),
}

#[derive(Diagnostic, Error, Debug)]
pub enum GenerateKeyError {
    #[error("Request not authorized")]
    Unauthorized,
    #[error("Request forbidden due to insufficient permissions")]
    Forbidden,
    #[error("Failed to generate IV: {0}")]
    GenerateIv(RecipherError),
    #[error("Received an invalid number of keys from request. Expected {expected} but received {received}")]
    InvalidNumberOfKeys { expected: usize, received: usize },
    // Catch-all for any `ViturRequestError` not classified as Forbidden /
    // Unauthorized above. Display surfaces the `kind` (operational enum
    // — `SendRequest`, `Other`, `ParseResponse`, ...) and `message`
    // (`&'static str`, build-time only, no dynamic data) so the bare
    // failure mode is visible. The dynamic `error: ShareableError` field
    // is reachable through `Error::source()` via `#[source]`, so callers
    // using anyhow chain formatting (`{:?}` / `{:#}`) or `tracing` get the
    // underlying transport / response error; the Display string itself
    // stays free of dynamic data.
    #[error("Unexpected error ({}: {})", .0.kind, .0.message)]
    RequestFailed(#[source] ViturRequestError),
}

impl From<ViturRequestError> for GenerateKeyError {
    fn from(err: ViturRequestError) -> Self {
        match err.kind {
            ViturRequestErrorKind::Forbidden => Self::Forbidden,
            ViturRequestErrorKind::Unauthorized => Self::Unauthorized,
            _ => Self::RequestFailed(err),
        }
    }
}

#[derive(Diagnostic, Error, Debug)]
pub enum EncryptError {
    #[error("Failed to generate key: {0}")]
    GenerateKey(#[from] GenerateKeyError),
    #[error("Failed to encrypt: {0}")]
    FailedToEncrypt(aes_gcm_siv::Error),
}

#[derive(Diagnostic, Error, Debug)]
pub enum DecryptError {
    #[error("Failed to decrypt single record: {0}")]
    RecordDecrypt(#[from] RecordDecryptError),
    #[error("Failed to retrieve key: {0}")]
    RetrieveKey(#[from] RetrieveKeyError),
    #[error("Failed to decrypt: {0}")]
    FailedToDecrypt(aes_gcm_siv::Error),
    #[error("Invalid ciphertext: {0}")]
    InvalidCiphertext(String),
    #[error("Internal error: {0}")]
    Internal(String),
    #[error(transparent)]
    SerializeError(#[from] rmp_serde::encode::Error),
    #[error(transparent)]
    DeserializeError(#[from] rmp_serde::decode::Error),
    #[error(transparent)]
    HexDecodingError(#[from] FromHexError),
    #[error(transparent)]
    Base85DecodingError(#[from] base85::Error),
    #[error(transparent)]
    CborError(#[from] serde_cbor::error::Error),
}

#[derive(Error, Debug, Clone)]
#[error("Failed to decrypt record: {reason}")]
pub struct RecordDecryptError {
    pub reason: String,
}

#[derive(Diagnostic, Error, Debug)]
pub enum SaveConfigError {
    #[error("Failed to generate new root key: {0}")]
    CreateRootKey(RecipherError),
    #[error("Failed to encrypt root key: {0}")]
    EncryptRootKey(EncryptError),
    #[error("Failed to decrypt root key: {0}")]
    DecryptRootKey(DecryptError),
    #[error("Failed to serialize encrypted root key: {0}")]
    SerializeEncryptedRootKey(DecryptError),
    #[error("Failed to deserialize encrypted root key: {0}")]
    DeserializeEncryptedRootKey(DecryptError),
    #[error("Invalid index root key length: {0}")]
    InvalidIndexRootKeySize(usize),
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum LoadConfigError {
    #[error("Failed to decrypt root key: {0}")]
    DecryptRootKey(#[from] DecryptError),
    #[error("Failed to deserialize encrypted root key: {0}")]
    DeserializeEncryptedRootKey(DecryptError),
    #[error("Invalid index root key length: {0}")]
    InvalidIndexRootKeySize(usize),
    #[error("Failed to send request: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

#[derive(Diagnostic, Error, Debug)]
pub enum LoadKeysetError {
    #[error("Failed to load keyset: {0}")]
    RequestFailed(#[from] ViturRequestError),
}

assert_impl_all!(CreateKeysetError: Send, Sync);
assert_impl_all!(GrantKeysetError: Send, Sync);
assert_impl_all!(RevokeKeysetError: Send, Sync);
assert_impl_all!(ListKeysetError: Send, Sync);
assert_impl_all!(CreateClientError: Send, Sync);
assert_impl_all!(ListClientError: Send, Sync);
assert_impl_all!(RevokeClientError: Send, Sync);
assert_impl_all!(RetrieveKeyError: Send, Sync);
assert_impl_all!(GenerateKeyError: Send, Sync);
assert_impl_all!(EncryptError: Send, Sync);
assert_impl_all!(DecryptError: Send, Sync);
assert_impl_all!(SaveConfigError: Send, Sync);
assert_impl_all!(LoadConfigError: Send, Sync);
assert_impl_all!(LoadKeysetError: Send, Sync);
