//! Macros for reducing service error boilerplate
//!
//! This module provides convenience macros to generate common error handling code
//! and reduce repetition across service-specific error implementations.

/// Generate a service error enum with CommonError wrapper and convenience constructors
///
/// This macro generates:
/// - An enum with a `Common(CommonError)` variant plus custom variants
/// - Constructor methods for each variant
/// - Automatic `From<CommonError>` implementation
///
/// # Example
///
/// ```rust
/// use common::error::define_service_error;
///
/// define_service_error! {
///     MyServiceError {
///         /// Custom error variant
///         CustomError { field: String, value: i32 },
///
///         /// Another custom variant
///         AnotherError(String),
///     }
/// }
/// ```
#[macro_export]
macro_rules! define_service_error {
    (
        $(#[$meta:meta])*
        $name:ident {
            $(
                $(#[$variant_meta:meta])*
                $variant:ident $({ $($field:ident : $field_ty:ty),* $(,)? })?
                $( ( $($tuple_ty:ty),* $(,)? ) )?
            ),* $(,)?
        }
    ) => {
        $(#[$meta])*
        #[derive(Debug, thiserror::Error)]
        pub enum $name {
            /// Common error with context
            #[error("Service error: {0}")]
            Common(#[from] $crate::error::CommonError),

            $(
                $(#[$variant_meta])*
                #[error("{}", stringify!($variant))]
                $variant $({ $($field: $field_ty,)* })? $( ( $($tuple_ty,)* ) )?,
            )*
        }

        impl From<$name> for $crate::error::CommonError {
            fn from(err: $name) -> Self {
                use $crate::error::ServiceErrorExt;
                err.to_common_error()
            }
        }
    };
}

/// Implement common convenience constructors for a service error
///
/// Generates constructors for common error types using `CommonError` builders.
///
/// # Example
///
/// ```rust
/// use common::error::impl_common_error_constructors;
///
/// impl_common_error_constructors!(MyServiceError);
///
/// // Now you can use:
/// // MyServiceError::network("connection failed")
/// // MyServiceError::configuration("missing key")
/// // MyServiceError::validation("field", "invalid value")
/// ```
#[macro_export]
macro_rules! impl_common_error_constructors {
    ($error_type:ty) => {
        impl $error_type {
            /// Create network error using CommonError
            pub fn network<M: Into<String>>(message: M) -> Self {
                Self::Common($crate::error::CommonError::network(message))
            }

            /// Create configuration error using CommonError
            pub fn configuration<M: Into<String>>(message: M) -> Self {
                Self::Common($crate::error::CommonError::config(message))
            }

            /// Create validation error using CommonError
            pub fn validation<F: Into<String>, M: Into<String>>(field: F, message: M) -> Self {
                Self::Common($crate::error::CommonError::validation(format!(
                    "{}: {}",
                    field.into(),
                    message.into()
                )))
            }

            /// Create timeout error using CommonError
            pub fn timeout(actual_ms: u64, max_ms: u64) -> Self {
                Self::Common($crate::error::CommonError::timeout(actual_ms, max_ms))
            }

            /// Create resource exhausted error using CommonError
            pub fn resource_exhausted<R: Into<String>>(resource: R) -> Self {
                Self::Common($crate::error::CommonError::resource_exhausted(resource))
            }

            /// Create internal error using CommonError
            pub fn internal<M: Into<String>>(message: M) -> Self {
                Self::Common($crate::error::CommonError::internal(message))
            }

            /// Create authentication error using CommonError
            pub fn authentication<M: Into<String>>(message: M) -> Self {
                Self::Common($crate::error::CommonError::service(
                    $crate::error::ErrorCategory::Authentication,
                    message.into(),
                ))
            }

            /// Create serialization error using CommonError
            pub fn serialization<M: Into<String>>(message: M) -> Self {
                Self::Common($crate::error::CommonError::serialization(message))
            }

            /// Create not found error using CommonError
            pub fn not_found<R: Into<String>, I: Into<String>>(resource: R, identifier: I) -> Self {
                Self::Common($crate::error::CommonError::service(
                    $crate::error::ErrorCategory::Resource,
                    format!("{} not found: {}", resource.into(), identifier.into()),
                ))
            }
        }
    };
}

/// Implement standard error conversions for service errors
///
/// Generates `From` implementations for common error types.
///
/// # Example
///
/// ```rust
/// use common::error::impl_error_conversions;
///
/// impl_error_conversions!(MyServiceError, [std::io::Error, serde_json::Error]);
/// ```
#[macro_export]
macro_rules! impl_error_conversions {
    ($error_type:ty, [$($from_type:ty),* $(,)?]) => {
        $(
            impl From<$from_type> for $error_type {
                fn from(err: $from_type) -> Self {
                    Self::Common($crate::error::CommonError::internal(format!("{}", err)))
                }
            }
        )*
    };
}

#[cfg(test)]
mod tests {
    use crate::error::{CommonError, ErrorCategory};

    define_service_error! {
        TestError {
            CustomError { message: String },
            TupleError(String, i32),
        }
    }

    impl_common_error_constructors!(TestError);

    #[test]
    fn test_define_service_error() {
        let _custom = TestError::CustomError {
            message: "test".to_string(),
        };
        let _tuple = TestError::TupleError("test".to_string(), 42);
        let _common = TestError::Common(CommonError::network("test"));
    }

    #[test]
    fn test_common_error_constructors() {
        let _network = TestError::network("connection failed");
        let _config = TestError::configuration("missing key");
        let _validation = TestError::validation("field", "invalid");
        let _timeout = TestError::timeout(5000, 2000);
    }

    #[test]
    fn test_error_conversion() {
        let common_err = CommonError::network("test");
        let service_err = TestError::Common(common_err);
        let _back_to_common: CommonError = service_err.into();
    }
}
