//! Variant definition

use syn::Ident;

use super::HttpStatusValue;

use super::field::FieldDefinition;
use super::field::TransparentFieldDefinition;
use super::format_string::validate_no_optional_fields_in_message;

/// A single variant of an error enum - either regular or transparent
#[derive(Debug, Clone)]
pub(crate) enum VariantDefinition {
    /// A regular error variant with message, code, and fields
    Regular(RegularVariantDefinition),

    /// A transparent variant that forwards to an inner error type
    Transparent(TransparentVariantDefinition),
}

impl VariantDefinition {
    /// Validate the variant definition
    pub(crate) fn validate(&self) -> syn::Result<()> {
        match self {
            Self::Regular(v) => v.validate(),
            Self::Transparent(v) => v.validate(),
        }
    }
}

/// A regular error variant with its own message, code, and status
#[derive(Debug, Clone)]
pub(crate) struct RegularVariantDefinition {
    /// The variant name
    pub(crate) name: Ident,

    /// Error message from `#[error("...")]`
    pub(crate) error_message: String,

    /// Diagnostic code from `#[diagnostic(code(...))]`
    pub(crate) diagnostic_code: String,

    /// Optional help text from `#[diagnostic(help(...))]`
    pub(crate) help_text: Option<String>,

    /// Optional URL from `#[diagnostic(url(...))]`
    pub(crate) url: Option<String>,

    /// Optional severity from `#[diagnostic(severity(...))]`
    pub(crate) severity: Option<String>,

    /// Optional HTTP status from `#[http_status(...)]`
    pub(crate) http_status: Option<HttpStatusValue>,

    /// Optional JSON-RPC error code from `#[jsonrpc_code(...)]`
    pub(crate) jsonrpc_code: Option<i32>,

    /// Fields of this variant
    pub(crate) fields: Vec<FieldDefinition>,
}

impl RegularVariantDefinition {
    /// Validate the variant definition
    fn validate(&self) -> syn::Result<()> {
        if self.error_message.is_empty() {
            return Err(syn::Error::new_spanned(
                &self.name,
                "variant must have #[error(\"message\")] attribute",
            ));
        }

        validate_no_optional_fields_in_message(&self.error_message, &self.fields)?;

        Ok(())
    }
}

/// A transparent variant that forwards to an inner error type
#[derive(Debug, Clone)]
pub(crate) struct TransparentVariantDefinition {
    /// The variant name
    pub(crate) name: Ident,

    /// The inner error type this variant forwards to
    pub(crate) field: TransparentFieldDefinition,
}

impl TransparentVariantDefinition {
    /// Validate the variant definition
    fn validate(&self) -> syn::Result<()> {
        // Transparent variants just need a valid inner type, which is ensured at parse time
        Ok(())
    }
}
