// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::HeadersError;

/// Represents errors that can occur during REX processing.
#[derive(
    Clone, Debug, Eq, Error, PartialEq, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
)]
pub enum RexError {
    #[error("Missing required parameter: {param}")]
    MissingParameter { param: String },

    #[error("Invalid parameter '{param}': {reason}")]
    InvalidParameter { param: String, reason: String },

    #[error("Failed to decode {encoding} for parameter '{param}': {reason}")]
    DecodeError {
        param: String,
        encoding: String,
        reason: String,
    },

    #[error("Invalid URL: {reason}")]
    InvalidUrl { reason: String },

    #[error("URL validation failed: {reason}")]
    UrlValidationError { reason: String },

    #[error("HTTP request failed: {message}{}", reason.as_ref().map(|r| format!(", {}", r)).unwrap_or_default()
    )]
    HttpRequestFailed {
        message: String,
        reason: Option<String>,
    },

    #[error("HTTP {status} error: {reason}")]
    HttpStatusError { status: u16, reason: String },

    #[error("Response size exceeds maximum of {max_size} bytes")]
    ResponseTooLarge { max_size: usize },

    #[error("Request body size of {size} bytes exceeds maximum of {max_size} bytes")]
    RequestBodyTooLarge { size: usize, max_size: usize },

    #[error("Filter error: {reason}")]
    FilterError { reason: String },

    #[error("Timeout after {duration_ms}ms")]
    Timeout { duration_ms: u64 },

    #[error("All {attempts} retry attempts failed: {reason}")]
    RetriesExhausted { attempts: u8, reason: String },

    #[error("JSON parsing error: {reason}")]
    JsonError { reason: String },

    #[error("No valid data sources available")]
    NoDataSources,

    #[error("Failed to decrypt secret payload: {reason}")]
    SecretDecryptionFailed { reason: String },

    #[error("Invalid secret payload format: {reason}")]
    InvalidSecretPayload { reason: String },

    #[error("Failed to generate secret key: {reason}")]
    SecretKeyGenerationFailed { reason: String },

    #[error("Failed to encrypt secret: {reason}")]
    SecretEncryptionFailed { reason: String },

    #[error("Provided REX output size is too small: {provided}")]
    RexOutputSizeTooSmall { provided: usize },

    #[error("Invalid REX duty input, err: {0}")]
    InvalidRexDutyInput(String),

    #[error("Connection error: {reason}")]
    ConnectionError { reason: String },

    #[error("WebSocket error: {message}")]
    WebSocketError { message: String },

    // DKG-specific errors
    #[error("DKG input validation failed: {reason}")]
    DkgValidationError { reason: String },

    #[error("DKG cryptographic operation failed ({operation}): {reason}")]
    DkgCryptoError { operation: String, reason: String },

    #[error("DKG handler not implemented: {handler}")]
    DkgNotImplemented { handler: String },

    #[error("DKG genesis failed: {reason}")]
    DkgGenesisError { reason: String },

    #[error("DKG share installation failed: {reason}")]
    DkgInstallShareError { reason: String },

    #[error("DKG share decryption (HPKE) failed for dealer {dealer_index}: {reason}")]
    DkgDecryptionError { dealer_index: u16, reason: String },

    #[error("DKG share verification failed for dealer {dealer_index}")]
    DkgVerificationError { dealer_index: u16 },

    #[error("DKG partial decryption failed: {reason}")]
    DkgPartialDecryptError { reason: String },

    #[error("DKG combine error: {reason}")]
    DkgCombineError { reason: String },

    #[error("DKG AEAD decryption failed: invalid ciphertext or authentication tag")]
    DkgAeadDecryptFailed,

    #[error("DKG key derivation (HKDF) failed")]
    DkgHkdfExpandFailed,

    #[error("DKG invalid key length")]
    DkgInvalidKeyLength,
    #[error("WASM execution error: {reason}")]
    WasmExecutionError { reason: String },
}

impl From<HeadersError> for RexError {
    fn from(err: HeadersError) -> Self {
        match err {
            HeadersError::DecodeError {
                param,
                encoding,
                reason,
            } => Self::DecodeError {
                param,
                encoding,
                reason,
            },
            HeadersError::JsonError { reason } => Self::JsonError { reason },
        }
    }
}
