// File: crates/smithy/src/lib.rs
// Module: smithy
// Public functions: 1

// crates/smithy/lib.rs - Fixed with proper optional type handling in flattening

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use smithy_core::{SmithyConstraint, SmithyEnumVariant, SmithyField};
use syn::{parse_macro_input, Attribute, DeriveInput, Fields, Lit, Meta, Variant};

/// Derive macro for generating Smithy models from Rust structs and enums
#[proc_macro_derive(SmithyModel, attributes(smithy))]
pub fn derive_smithy_model(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    match generate_smithy_impl(&input) {
        Ok(tokens) => tokens.into(),
        Err(err) => err.to_compile_error().into(),
    }
}

fn generate_smithy_impl(input: &DeriveInput) -> syn::Result<TokenStream2> {
    let name = &input.ident;
    let (namespace, is_mixin) = extract_namespace_and_mixin(&input.attrs)?;

    match &input.data {
        syn::Data::Struct(data_struct) => {
            generate_struct_impl(name, &namespace, data_struct, &input.attrs, is_mixin)
        }
        syn::Data::Enum(data_enum) => generate_enum_impl(name, &namespace, data_enum, &input.attrs),
        _ => Err(syn::Error::new_spanned(
            input,
            "SmithyModel can only be derived for structs and enums",
        )),
    }
}

fn generate_struct_impl(
    name: &syn::Ident,
    namespace: &str,
    data_struct: &syn::DataStruct,
    attrs: &[Attribute],
    is_mixin: bool,
) -> syn::Result<TokenStream2> {
    let fields = extract_fields(&data_struct.fields)?;

    let struct_doc = extract_documentation(attrs);
    let struct_doc_expr = struct_doc
        .as_ref()
        .map(|doc| quote! { Some(#doc.to_string()) })
        .unwrap_or(quote! { None });

    let field_implementations = fields.iter().map(|field| {
        let field_name = &field.name;
        let value_type = &field.value_type;
        let documentation = &field.documentation;
        let constraints = &field.constraints;
        let optional = field.optional;
        let flatten = field.flatten;

        if flatten {
            // Extract the inner type from Option<T> if it's an optional type
            let inner_type = if value_type.starts_with("Option<") && value_type.ends_with('>') {
                let start_idx = "Option<".len();
                let end_idx = value_type.len() - 1;
                &value_type[start_idx..end_idx]
            } else {
                value_type
            };

            let inner_type_ident = syn::parse_str::<syn::Type>(inner_type).unwrap();
            // For flattened fields, we merge the fields from the inner type
            // but we don't add the field itself to the structure
            quote! {
                {
                    let flattened_model = <#inner_type_ident as smithy_core::SmithyModelGenerator>::generate_smithy_model();
                    let flattened_struct_name = stringify!(#inner_type_ident).to_string();

                    for (shape_name, shape) in flattened_model.shapes {
                        if shape_name == flattened_struct_name {
                            match shape {
                                smithy_core::SmithyShape::Structure { members: flattened_members, .. } |
                                smithy_core::SmithyShape::Union { members: flattened_members, .. } => {
                                    members.extend(flattened_members);
                                }
                                _ => {
                                    // Potentially handle other shapes or log a warning
                                }
                            }
                        } else {
                            shapes.insert(shape_name, shape);
                        }
                    }
                }
            }
        } else {

            let field_doc = documentation
                .as_ref()
                .map(|doc| quote! { Some(#doc.to_string()) })
                .unwrap_or(quote! { None });

            let mut all_constraints = constraints.clone();
            if !optional && !all_constraints.iter().any(|c| matches!(c, SmithyConstraint::Required)) {
                all_constraints.push(SmithyConstraint::Required);
            }

            let traits = if all_constraints.is_empty() {
                quote! { vec![] }
            } else {
                let trait_tokens = all_constraints
                    .iter()
                    .map(|constraint| match constraint {
                        SmithyConstraint::Pattern(pattern) => quote! {
                            smithy_core::SmithyTrait::Pattern { pattern: #pattern.to_string() }
                        },
                        SmithyConstraint::Range(min, max) => {
                            let min_expr = min.map(|v| quote! { Some(#v) }).unwrap_or(quote! { None });
                            let max_expr = max.map(|v| quote! { Some(#v) }).unwrap_or(quote! { None });
                            quote! {
                                smithy_core::SmithyTrait::Range {
                                    min: #min_expr,
                                    max: #max_expr
                                }
                            }
                        },
                        SmithyConstraint::Length(min, max) => {
                            let min_expr = min.map(|v| quote! { Some(#v) }).unwrap_or(quote! { None });
                            let max_expr = max.map(|v| quote! { Some(#v) }).unwrap_or(quote! { None });
                            quote! {
                                smithy_core::SmithyTrait::Length {
                                    min: #min_expr,
                                    max: #max_expr
                                }
                            }
                        },
                        SmithyConstraint::Required => quote! {
                            smithy_core::SmithyTrait::Required
                        },
                        SmithyConstraint::HttpLabel => quote! {
                            smithy_core::SmithyTrait::HttpLabel
                        },
                        SmithyConstraint::HttpQuery(name) => quote! {
                            smithy_core::SmithyTrait::HttpQuery { name: #name.to_string() }
                        },
                    })
                    .collect::<Vec<_>>();

                quote! { vec![#(#trait_tokens),*] }
            };

            quote! {
                {
                    let (target_type, new_shapes) = smithy_core::types::resolve_type_and_generate_shapes(#value_type, &mut shapes).unwrap();
                    shapes.extend(new_shapes);
                    members.insert(#field_name.to_string(), smithy_core::SmithyMember {
                        target: target_type,
                        documentation: #field_doc,
                        traits: #traits,
                    });
                }
            }
        }
    });

    let traits_expr = if is_mixin {
        quote! { vec![smithy_core::SmithyTrait::Mixin] }
    } else {
        quote! { vec![] }
    };

    let expanded = quote! {
        impl smithy_core::SmithyModelGenerator for #name {
            fn generate_smithy_model() -> smithy_core::SmithyModel {
                let mut shapes = std::collections::HashMap::new();
                let mut members = std::collections::HashMap::new();

                #(#field_implementations;)*

                let shape = smithy_core::SmithyShape::Structure {
                    members,
                    documentation: #struct_doc_expr,
                    traits: #traits_expr
                };

                shapes.insert(stringify!(#name).to_string(), shape);

                smithy_core::SmithyModel {
                    namespace: #namespace.to_string(),
                    shapes
                }
            }
        }
    };

    Ok(expanded)
}

fn generate_enum_impl(
    name: &syn::Ident,
    namespace: &str,
    data_enum: &syn::DataEnum,
    attrs: &[Attribute],
) -> syn::Result<TokenStream2> {
    let variants = extract_enum_variants(&data_enum.variants)?;
    let serde_enum_attrs = parse_serde_enum_attributes(attrs)?;

    let enum_doc = extract_documentation(attrs);
    let enum_doc_expr = enum_doc
        .as_ref()
        .map(|doc| quote! { Some(#doc.to_string()) })
        .unwrap_or(quote! { None });

    // Check if this is a string enum (all variants are unit variants) or a union
    let is_string_enum = variants.iter().all(|v| v.fields.is_empty());

    if is_string_enum {
        // Generate as Smithy enum
        let variant_implementations = variants
            .iter()
            .map(|variant| {
                let variant_name = &variant.name;
                let variant_doc = variant
                    .documentation
                    .as_ref()
                    .map(|doc| quote! { Some(#doc.to_string()) })
                    .unwrap_or(quote! { None });

                // Apply serde rename transformation if specified
                let rename_all = serde_enum_attrs.rename_all.as_deref();
                let transformed_name = if let Some(rename_pattern) = rename_all {
                    // Generate the transformation at compile time
                    let transformed = transform_variant_name(variant_name, Some(rename_pattern));
                    quote! { #transformed.to_string() }
                } else {
                    quote! { #variant_name.to_string() }
                };

                quote! {
                    enum_values.insert(#transformed_name, smithy_core::SmithyEnumValue {
                        name: #transformed_name,
                        documentation: #variant_doc,
                        is_default: false,
                    });
                }
            })
            .collect::<Vec<_>>();

        let expanded = quote! {
            impl smithy_core::SmithyModelGenerator for #name {
                fn generate_smithy_model() -> smithy_core::SmithyModel {
                    let mut shapes = std::collections::HashMap::new();
                    let mut enum_values = std::collections::HashMap::new();

                    #(#variant_implementations)*

                    let shape = smithy_core::SmithyShape::Enum {
                        values: enum_values,
                        documentation: #enum_doc_expr,
                        traits: vec![]
                    };

                    shapes.insert(stringify!(#name).to_string(), shape);

                    smithy_core::SmithyModel {
                        namespace: #namespace.to_string(),
                        shapes
                    }
                }
            }
        };

        Ok(expanded)
    } else {
        // Generate as Smithy union
        let variant_implementations = variants
            .iter()
            .filter_map(|variant| {
                let variant_name = &variant.name;
                let variant_doc = variant
                    .documentation
                    .as_ref()
                    .map(|doc| quote! { Some(#doc.to_string()) })
                    .unwrap_or(quote! { None });

                let target_type_expr = if variant.fields.is_empty() {
                    // If there are no fields with `value_type`, this variant should be skipped.
                    return None;
                } else if variant.fields.len() == 1 {
                    // Single field - reference the type directly instead of creating a wrapper
                    let field = &variant.fields[0];
                    let field_value_type = &field.value_type;
                    if field_value_type.is_empty() {
                        return None;
                    }

                    quote! {
                        {
                            let (target_type, new_shapes) = smithy_core::types::resolve_type_and_generate_shapes(#field_value_type, &mut shapes).unwrap();
                            shapes.extend(new_shapes);
                            target_type
                        }
                    }
                } else {
                    // Multiple fields - create an inline structure
                    let inline_struct_members = variant.fields.iter().map(|field| {
                        let field_name = &field.name;
                        let field_value_type = &field.value_type;
                        let field_doc = field
                            .documentation
                            .as_ref()
                            .map(|doc| quote! { Some(#doc.to_string()) })
                            .unwrap_or(quote! { None });

                        let mut field_constraints = field.constraints.clone();
                        if !field.optional && !field_constraints.iter().any(|c| matches!(c, SmithyConstraint::Required)) {
                            field_constraints.push(SmithyConstraint::Required);
                        }

                        let field_traits = if field_constraints.is_empty() {
                            quote! { vec![] }
                        } else {
                            let trait_tokens = field_constraints
                                .iter()
                                .map(|constraint| match constraint {
                                    SmithyConstraint::Pattern(pattern) => quote! {
                                        smithy_core::SmithyTrait::Pattern { pattern: #pattern.to_string() }
                                    },
                                    SmithyConstraint::Range(min, max) => {
                                        let min_expr = min.map(|v| quote! { Some(#v) }).unwrap_or(quote! { None });
                                        let max_expr = max.map(|v| quote! { Some(#v) }).unwrap_or(quote! { None });
                                        quote! {
                                            smithy_core::SmithyTrait::Range {
                                                min: #min_expr,
                                                max: #max_expr
                                            }
                                        }
                                    },
                                    SmithyConstraint::Length(min, max) => {
                                        let min_expr = min.map(|v| quote! { Some(#v) }).unwrap_or(quote! { None });
                                        let max_expr = max.map(|v| quote! { Some(#v) }).unwrap_or(quote! { None });
                                        quote! {
                                            smithy_core::SmithyTrait::Length {
                                                min: #min_expr,
                                                max: #max_expr
                                            }
                                        }
                                    },
                                    SmithyConstraint::Required => quote! {
                                        smithy_core::SmithyTrait::Required
                                    },
                                    SmithyConstraint::HttpLabel => quote! {
                                        smithy_core::SmithyTrait::HttpLabel
                                    },
                                    SmithyConstraint::HttpQuery(name) => quote! {
                                        smithy_core::SmithyTrait::HttpQuery { name: #name.to_string() }
                                    },
                                })
                                .collect::<Vec<_>>();

                            quote! { vec![#(#trait_tokens),*] }
                        };

                        quote! {
                            {
                                let (field_target, field_shapes) = smithy_core::types::resolve_type_and_generate_shapes(#field_value_type, &mut shapes).unwrap();
                                shapes.extend(field_shapes);
                                inline_members.insert(#field_name.to_string(), smithy_core::SmithyMember {
                                    target: field_target,
                                    documentation: #field_doc,
                                    traits: #field_traits,
                                });
                            }
                        }
                    });

                    quote! {
                        {
                            let inline_struct_name = format!("{}{}Data", stringify!(#name), #variant_name);
                            let mut inline_members = std::collections::HashMap::new();
                            #(#inline_struct_members)*
                            let inline_shape = smithy_core::SmithyShape::Structure {
                                members: inline_members,
                                documentation: None,
                                traits: vec![],
                            };
                            shapes.insert(inline_struct_name.clone(), inline_shape);
                            inline_struct_name
                        }
                    }
                };

                // Apply serde rename transformation if specified
                let rename_all = serde_enum_attrs.rename_all.as_deref();
                let transformed_name = if let Some(rename_pattern) = rename_all {
                    // Generate the transformation at compile time
                    let transformed = transform_variant_name(variant_name, Some(rename_pattern));
                    quote! { #transformed.to_string() }
                } else {
                    quote! { #variant_name.to_string() }
                };

                Some(quote! {
                    let target_type = #target_type_expr;
                    members.insert(#transformed_name, smithy_core::SmithyMember {
                        target: target_type,
                        documentation: #variant_doc,
                        traits: vec![]
                    });
                })
            })
            .collect::<Vec<_>>();

        let expanded = quote! {
            impl smithy_core::SmithyModelGenerator for #name {
                fn generate_smithy_model() -> smithy_core::SmithyModel {
                    let mut shapes = std::collections::HashMap::new();
                    let mut members = std::collections::HashMap::new();

                    #(#variant_implementations;)*

                    let shape = smithy_core::SmithyShape::Union {
                        members,
                        documentation: #enum_doc_expr,
                        traits: vec![]
                    };

                    shapes.insert(stringify!(#name).to_string(), shape);

                    smithy_core::SmithyModel {
                        namespace: #namespace.to_string(),
                        shapes
                    }
                }
            }
        };

        Ok(expanded)
    }
}

fn extract_namespace_and_mixin(attrs: &[Attribute]) -> syn::Result<(String, bool)> {
    for attr in attrs {
        if attr.path().is_ident("smithy") {
            let mut namespace = None;
            let mut mixin = false;
            attr.parse_nested_meta(|meta| {
                if meta.path.is_ident("namespace") {
                    if let Ok(value) = meta.value() {
                        if let Ok(Lit::Str(lit_str)) = value.parse::<Lit>() {
                            namespace = Some(lit_str.value());
                        }
                    }
                } else if meta.path.is_ident("mixin") {
                    if let Ok(value) = meta.value() {
                        if let Ok(Lit::Bool(lit_bool)) = value.parse::<Lit>() {
                            mixin = lit_bool.value;
                        }
                    }
                }
                Ok(())
            })?; // Propagate parsing errors

            return Ok((
                namespace.unwrap_or_else(|| "com.hyperswitch.default".to_string()),
                mixin,
            ));
        }
    }
    Ok(("com.hyperswitch.default".to_string(), false))
}

fn extract_fields(fields: &Fields) -> syn::Result<Vec<SmithyField>> {
    let mut smithy_fields = Vec::new();

    match fields {
        Fields::Named(fields_named) => {
            for field in &fields_named.named {
                let field_name = field.ident.as_ref().unwrap().to_string();
                let field_attrs = parse_smithy_field_attributes(&field.attrs)?;
                let serde_attrs = parse_serde_attributes(&field.attrs)?;

                if let Some(value_type) = field_attrs.value_type {
                    let documentation = extract_documentation(&field.attrs);
                    let optional = value_type.trim().starts_with("Option<");

                    smithy_fields.push(SmithyField {
                        name: field_name,
                        value_type,
                        constraints: field_attrs.constraints,
                        documentation,
                        optional,
                        flatten: serde_attrs.flatten,
                    });
                }
            }
        }
        _ => {
            return Err(syn::Error::new_spanned(
                fields,
                "Only named fields are supported",
            ))
        }
    }

    Ok(smithy_fields)
}

fn extract_enum_variants(
    variants: &syn::punctuated::Punctuated<Variant, syn::token::Comma>,
) -> syn::Result<Vec<SmithyEnumVariant>> {
    let mut smithy_variants = Vec::new();

    for variant in variants {
        let variant_name = variant.ident.to_string();
        let documentation = extract_documentation(&variant.attrs);
        let variant_attrs = parse_smithy_field_attributes(&variant.attrs)?;

        // Extract fields from the variant
        let fields = match &variant.fields {
            Fields::Unit => Vec::new(),
            Fields::Named(fields_named) => {
                let mut variant_fields = Vec::new();
                for field in &fields_named.named {
                    let field_name = field.ident.as_ref().unwrap().to_string();
                    let field_attrs = parse_smithy_field_attributes(&field.attrs)?;

                    if let Some(value_type) = field_attrs.value_type {
                        let field_documentation = extract_documentation(&field.attrs);
                        let optional = value_type.trim().starts_with("Option<");

                        variant_fields.push(SmithyField {
                            name: field_name,
                            value_type,
                            constraints: field_attrs.constraints,
                            documentation: field_documentation,
                            optional,
                            flatten: false,
                        });
                    }
                }
                variant_fields
            }
            Fields::Unnamed(fields_unnamed) => {
                let mut variant_fields = Vec::new();
                for (index, field) in fields_unnamed.unnamed.iter().enumerate() {
                    let field_name = format!("field_{}", index);
                    let field_attrs = parse_smithy_field_attributes(&field.attrs)?;

                    // For single unnamed fields, use the variant attribute if field doesn't have one
                    let value_type = field_attrs
                        .value_type
                        .or_else(|| variant_attrs.value_type.clone());

                    if let Some(value_type) = value_type {
                        let field_documentation = extract_documentation(&field.attrs);
                        let optional = value_type.trim().starts_with("Option<");

                        variant_fields.push(SmithyField {
                            name: field_name,
                            value_type,
                            constraints: field_attrs.constraints,
                            documentation: field_documentation,
                            optional,
                            flatten: false,
                        });
                    }
                }
                variant_fields
            }
        };

        smithy_variants.push(SmithyEnumVariant {
            name: variant_name,
            fields,
            constraints: variant_attrs.constraints,
            documentation,
        });
    }

    Ok(smithy_variants)
}

#[derive(Default)]
struct SmithyFieldAttributes {
    value_type: Option<String>,
    constraints: Vec<SmithyConstraint>,
}

#[derive(Default)]
struct SerdeAttributes {
    flatten: bool,
}

#[derive(Default)]
struct SerdeEnumAttributes {
    rename_all: Option<String>,
}

fn parse_serde_attributes(attrs: &[Attribute]) -> syn::Result<SerdeAttributes> {
    let mut serde_attributes = SerdeAttributes::default();

    for attr in attrs {
        if attr.path().is_ident("serde") {
            if let Ok(list) = attr.meta.require_list() {
                if list.path.is_ident("serde") {
                    for item in list.tokens.clone() {
                        if let Some(ident) = item.to_string().split_whitespace().next() {
                            if ident == "flatten" {
                                serde_attributes.flatten = true;
                            }
                        }
                    }
                }
            }
        }
    }

    Ok(serde_attributes)
}

fn parse_serde_enum_attributes(attrs: &[Attribute]) -> syn::Result<SerdeEnumAttributes> {
    let mut serde_enum_attributes = SerdeEnumAttributes::default();

    for attr in attrs {
        if attr.path().is_ident("serde") {
            // Use more robust parsing that handles all serde attributes
            let parse_result = attr.parse_nested_meta(|meta| {
                if meta.path.is_ident("rename_all") {
                    if let Ok(value) = meta.value() {
                        if let Ok(Lit::Str(lit_str)) = value.parse::<Lit>() {
                            serde_enum_attributes.rename_all = Some(lit_str.value());
                        }
                    }
                } else if meta.path.is_ident("tag") {
                    // Parse and ignore the tag attribute
                    if let Ok(value) = meta.value() {
                        let _ = value.parse::<Lit>();
                    }
                } else if meta.path.is_ident("content") {
                    // Parse and ignore the content attribute
                    if let Ok(value) = meta.value() {
                        let _ = value.parse::<Lit>();
                    }
                } else if meta.path.is_ident("rename") {
                    // Parse and ignore the rename attribute (used for enum renaming)
                    if let Ok(value) = meta.value() {
                        let _ = value.parse::<Lit>();
                    }
                } else if meta.path.is_ident("deny_unknown_fields") {
                    // Handle deny_unknown_fields (no value needed)
                    // This is a flag attribute with no value
                } else if meta.path.is_ident("skip_serializing") {
                    // Handle skip_serializing
                } else if meta.path.is_ident("skip_deserializing") {
                    // Handle skip_deserializing
                } else if meta.path.is_ident("skip_serializing_if") {
                    // Handle skip_serializing_if
                    if let Ok(value) = meta.value() {
                        let _ = value.parse::<syn::Expr>();
                    }
                } else if meta.path.is_ident("default") {
                    // Handle default attribute
                    // Could have a value or be a flag
                    if meta.value().is_ok() {
                        let _ = meta.value().and_then(|v| v.parse::<syn::Expr>());
                    }
                } else if meta.path.is_ident("flatten") {
                    // Handle flatten (flag attribute)
                } else if meta.path.is_ident("untagged") {
                    // Handle untagged (flag attribute)
                } else if meta.path.is_ident("bound") {
                    // Handle bound attribute
                    if let Ok(value) = meta.value() {
                        let _ = value.parse::<Lit>();
                    }
                }
                // Silently ignore any other serde attributes to prevent parsing errors
                Ok(())
            });

            // If parsing failed, provide a more helpful error message
            if let Err(e) = parse_result {
                return Err(syn::Error::new_spanned(
                    attr,
                    format!("Failed to parse serde attribute: {}. This may be due to multiple serde attributes on separate lines. Consider consolidating them into a single #[serde(...)] attribute.", e)
                ));
            }
        }
    }

    Ok(serde_enum_attributes)
}

fn transform_variant_name(name: &str, rename_all: Option<&str>) -> String {
    match rename_all {
        Some("snake_case") => to_snake_case(name),
        Some("camelCase") => to_camel_case(name),
        Some("kebab-case") => to_kebab_case(name),
        Some("PascalCase") => name.to_string(), // No change for PascalCase
        Some("SCREAMING_SNAKE_CASE") => to_screaming_snake_case(name),
        Some("lowercase") => name.to_lowercase(),
        Some("UPPERCASE") => name.to_uppercase(),
        _ => name.to_string(), // No transformation if no rename_all or unknown pattern
    }
}

fn to_snake_case(input: &str) -> String {
    let mut result = String::new();
    let chars = input.chars();

    for ch in chars {
        if ch.is_uppercase() && !result.is_empty() {
            // Add underscore before uppercase letters (except the first character)
            result.push('_');
        }
        result.push(ch.to_lowercase().next().unwrap());
    }

    result
}

fn to_camel_case(input: &str) -> String {
    let mut result = String::new();
    let mut chars = input.chars();

    // First character should be lowercase
    if let Some(ch) = chars.next() {
        result.push(ch.to_lowercase().next().unwrap());
    }

    // Rest of the characters remain the same
    for ch in chars {
        result.push(ch);
    }

    result
}

fn to_kebab_case(input: &str) -> String {
    let mut result = String::new();

    for ch in input.chars() {
        if ch.is_uppercase() && !result.is_empty() {
            // Add hyphen before uppercase letters (except the first character)
            result.push('-');
        }
        result.push(ch.to_lowercase().next().unwrap());
    }

    result
}

fn to_screaming_snake_case(input: &str) -> String {
    let mut result = String::new();

    for ch in input.chars() {
        if ch.is_uppercase() && !result.is_empty() {
            // Add underscore before uppercase letters (except the first character)
            result.push('_');
        }
        result.push(ch.to_uppercase().next().unwrap());
    }

    result
}

fn parse_smithy_field_attributes(attrs: &[Attribute]) -> syn::Result<SmithyFieldAttributes> {
    let mut field_attributes = SmithyFieldAttributes::default();

    for attr in attrs {
        if attr.path().is_ident("smithy") {
            attr.parse_nested_meta(|meta| {
                if meta.path.is_ident("value_type") {
                    if let Ok(value) = meta.value() {
                        if let Ok(Lit::Str(lit_str)) = value.parse::<Lit>() {
                            field_attributes.value_type = Some(lit_str.value());
                        }
                    }
                } else if meta.path.is_ident("pattern") {
                    if let Ok(value) = meta.value() {
                        if let Ok(Lit::Str(lit_str)) = value.parse::<Lit>() {
                            field_attributes
                                .constraints
                                .push(SmithyConstraint::Pattern(lit_str.value()));
                        }
                    }
                } else if meta.path.is_ident("range") {
                    if let Ok(value) = meta.value() {
                        if let Ok(Lit::Str(lit_str)) = value.parse::<Lit>() {
                            let range_str = lit_str.value();
                            match parse_range(&range_str) {
                                Ok((min, max)) => {
                                    field_attributes
                                        .constraints
                                        .push(SmithyConstraint::Range(min, max));
                                }
                                Err(e) => {
                                    return Err(syn::Error::new_spanned(
                                        &meta.path,
                                        format!("Invalid range: {}", e),
                                    ));
                                }
                            }
                        }
                    }
                } else if meta.path.is_ident("length") {
                    if let Ok(value) = meta.value() {
                        if let Ok(Lit::Str(lit_str)) = value.parse::<Lit>() {
                            let length_str = lit_str.value();
                            match parse_length(&length_str) {
                                Ok((min, max)) => {
                                    field_attributes
                                        .constraints
                                        .push(SmithyConstraint::Length(min, max));
                                }
                                Err(e) => {
                                    return Err(syn::Error::new_spanned(
                                        &meta.path,
                                        format!("Invalid length: {}", e),
                                    ));
                                }
                            }
                        }
                    }
                } else if meta.path.is_ident("required") {
                    field_attributes
                        .constraints
                        .push(SmithyConstraint::Required);
                } else if meta.path.is_ident("http_label") {
                    field_attributes
                        .constraints
                        .push(SmithyConstraint::HttpLabel);
                } else if meta.path.is_ident("http_query") {
                    if let Ok(value) = meta.value() {
                        if let Ok(Lit::Str(lit_str)) = value.parse::<Lit>() {
                            field_attributes
                                .constraints
                                .push(SmithyConstraint::HttpQuery(lit_str.value()));
                        }
                    }
                }
                Ok(())
            })?;
        }
    }

    // Automatically add Required for http_label fields
    if field_attributes
        .constraints
        .iter()
        .any(|c| matches!(c, SmithyConstraint::HttpLabel))
        && !field_attributes
            .constraints
            .iter()
            .any(|c| matches!(c, SmithyConstraint::Required))
    {
        field_attributes
            .constraints
            .push(SmithyConstraint::Required);
    }

    Ok(field_attributes)
}

fn extract_documentation(attrs: &[Attribute]) -> Option<String> {
    let mut docs = Vec::new();

    for attr in attrs {
        if attr.path().is_ident("doc") {
            if let Meta::NameValue(meta_name_value) = &attr.meta {
                if let syn::Expr::Lit(expr_lit) = &meta_name_value.value {
                    if let Lit::Str(lit_str) = &expr_lit.lit {
                        docs.push(lit_str.value().trim().to_string());
                    }
                }
            }
        }
    }

    if docs.is_empty() {
        None
    } else {
        Some(docs.join(" "))
    }
}

fn parse_range(range_str: &str) -> Result<(Option<i64>, Option<i64>), String> {
    if range_str.contains("..=") {
        let parts: Vec<&str> = range_str.split("..=").collect();
        if parts.len() != 2 {
            return Err(
                "Invalid range format: must be 'min..=max', '..=max', or 'min..='".to_string(),
            );
        }
        let min = if parts[0].is_empty() {
            None
        } else {
            Some(
                parts[0]
                    .parse()
                    .map_err(|_| format!("Invalid range min: '{}'", parts[0]))?,
            )
        };
        let max = if parts[1].is_empty() {
            None
        } else {
            Some(
                parts[1]
                    .parse()
                    .map_err(|_| format!("Invalid range max: '{}'", parts[1]))?,
            )
        };
        Ok((min, max))
    } else if range_str.contains("..") {
        let parts: Vec<&str> = range_str.split("..").collect();
        if parts.len() != 2 {
            return Err(
                "Invalid range format: must be 'min..max', '..max', or 'min..'".to_string(),
            );
        }
        let min = if parts[0].is_empty() {
            None
        } else {
            Some(
                parts[0]
                    .parse()
                    .map_err(|_| format!("Invalid range min: '{}'", parts[0]))?,
            )
        };
        let max = if parts[1].is_empty() {
            None
        } else {
            Some(
                parts[1]
                    .parse::<i64>()
                    .map_err(|_| format!("Invalid range max: '{}'", parts[1]))?
                    - 1,
            )
        };
        Ok((min, max))
    } else {
        Err("Invalid range format: must contain '..' or '..='".to_string())
    }
}

fn parse_length(length_str: &str) -> Result<(Option<u64>, Option<u64>), String> {
    if length_str.contains("..=") {
        let parts: Vec<&str> = length_str.split("..=").collect();
        if parts.len() != 2 {
            return Err(
                "Invalid length format: must be 'min..=max', '..=max', or 'min..='".to_string(),
            );
        }
        let min = if parts[0].is_empty() {
            None
        } else {
            Some(
                parts[0]
                    .parse()
                    .map_err(|_| format!("Invalid length min: '{}'", parts[0]))?,
            )
        };
        let max = if parts[1].is_empty() {
            None
        } else {
            Some(
                parts[1]
                    .parse()
                    .map_err(|_| format!("Invalid length max: '{}'", parts[1]))?,
            )
        };
        Ok((min, max))
    } else if length_str.contains("..") {
        let parts: Vec<&str> = length_str.split("..").collect();
        if parts.len() != 2 {
            return Err(
                "Invalid length format: must be 'min..max', '..max', or 'min..'".to_string(),
            );
        }
        let min = if parts[0].is_empty() {
            None
        } else {
            Some(
                parts[0]
                    .parse()
                    .map_err(|_| format!("Invalid length min: '{}'", parts[0]))?,
            )
        };
        let max = if parts[1].is_empty() {
            None
        } else {
            Some(
                parts[1]
                    .parse::<u64>()
                    .map_err(|_| format!("Invalid length max: '{}'", parts[1]))?
                    - 1,
            )
        };
        Ok((min, max))
    } else {
        Err("Invalid length format: must contain '..' or '..='".to_string())
    }
}


// Struct: CreditCardData
// File: crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CreditCardData


// Function: get_dispute_filter_for_dimension
// File: crates/analytics/src/disputes/filters.rs
// Module: analytics

pub fn get_dispute_filter_for_dimension<T>(
    dimension: DisputeDimensions,
    auth: &AuthInfo,
    time_range: &TimeRange,
    pool: &T,
) -> FiltersResult<Vec<DisputeFilterRow>>
where
    T: AnalyticsDataSource + DisputeFilterAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,


// File: crates/diesel_models/src/role.rs
// Module: diesel_models
// Public structs: 3

use common_utils::id_type;
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use time::PrimitiveDateTime;

use crate::{enums, schema::roles};

#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
#[diesel(table_name = roles, primary_key(role_id), check_for_backend(diesel::pg::Pg))]
pub struct Role {
    pub role_name: String,
    pub role_id: String,
    pub merchant_id: Option<id_type::MerchantId>,
    pub org_id: id_type::OrganizationId,
    #[diesel(deserialize_as = super::DieselArray<enums::PermissionGroup>)]
    pub groups: Vec<enums::PermissionGroup>,
    pub scope: enums::RoleScope,
    pub created_at: PrimitiveDateTime,
    pub created_by: String,
    pub last_modified_at: PrimitiveDateTime,
    pub last_modified_by: String,
    pub entity_type: enums::EntityType,
    pub profile_id: Option<id_type::ProfileId>,
    pub tenant_id: id_type::TenantId,
}

#[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
#[diesel(table_name = roles)]
pub struct RoleNew {
    pub role_name: String,
    pub role_id: String,
    pub merchant_id: Option<id_type::MerchantId>,
    pub org_id: id_type::OrganizationId,
    #[diesel(deserialize_as = super::DieselArray<enums::PermissionGroup>)]
    pub groups: Vec<enums::PermissionGroup>,
    pub scope: enums::RoleScope,
    pub created_at: PrimitiveDateTime,
    pub created_by: String,
    pub last_modified_at: PrimitiveDateTime,
    pub last_modified_by: String,
    pub entity_type: enums::EntityType,
    pub profile_id: Option<id_type::ProfileId>,
    pub tenant_id: id_type::TenantId,
}

#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
#[diesel(table_name = roles)]
pub struct RoleUpdateInternal {
    groups: Option<Vec<enums::PermissionGroup>>,
    role_name: Option<String>,
    last_modified_by: String,
    last_modified_at: PrimitiveDateTime,
}

pub enum RoleUpdate {
    UpdateDetails {
        groups: Option<Vec<enums::PermissionGroup>>,
        role_name: Option<String>,
        last_modified_at: PrimitiveDateTime,
        last_modified_by: String,
    },
}

impl From<RoleUpdate> for RoleUpdateInternal {
    fn from(value: RoleUpdate) -> Self {
        match value {
            RoleUpdate::UpdateDetails {
                groups,
                role_name,
                last_modified_by,
                last_modified_at,
            } => Self {
                groups,
                role_name,
                last_modified_at,
                last_modified_by,
            },
        }
    }
}

#[derive(Clone, Debug)]
pub enum ListRolesByEntityPayload {
    Profile(id_type::MerchantId, id_type::ProfileId),
    Merchant(id_type::MerchantId),
    Organization,
}


// Function: get_three_ds_decision_rule_keys
// File: crates/euclid_wasm/src/lib.rs
// Module: euclid_wasm

pub fn get_three_ds_decision_rule_keys() -> JsResult


// Function: update_resource
// File: crates/storage_impl/src/kv_router_store.rs
// Module: storage_impl

pub fn update_resource<D, R, M>(
        &self,
        state: &KeyManagerState,
        key_store: &MerchantKeyStore,
        storage_scheme: MerchantStorageScheme,
        update_resource_fut: R,
        updated_resource: M,
        UpdateResourceParams {
            updateable,
            operation,
        }: UpdateResourceParams<'_>,
    ) -> error_stack::Result<D, errors::StorageError>
    where
        D: Debug + Sync + Conversion,
        M: StorageModel<D>,
        R: futures::Future<Output = error_stack::Result<M, DatabaseError>> + Send,


// Function: find_optional_by_address_id
// File: crates/diesel_models/src/query/address.rs
// Module: diesel_models

pub fn find_optional_by_address_id(
        conn: &PgPooledConn,
        address_id: &str,
    ) -> StorageResult<Option<Self>>


// Struct: ThreeDs
// File: crates/hyperswitch_connectors/src/connectors/payme/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreeDs


// Function: find_by_merchant_id_fingerprint_id
// File: crates/diesel_models/src/query/blocklist_fingerprint.rs
// Module: diesel_models

pub fn find_by_merchant_id_fingerprint_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        fingerprint_id: &str,
    ) -> StorageResult<Self>


// Struct: CoingateRouterData
// File: crates/hyperswitch_connectors/src/connectors/coingate/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CoingateRouterData<T>


    pub id: String,
    pub amount: i64,
    pub currency: String,
    pub charge: String,
    pub status: Shift4RefundStatus,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Shift4RefundStatus {
    Successful,
    Processing,
    #[default]
    Failed,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for RefundsRouterData<Execute> {
    type Error = Error;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::from(item.response.status);
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id,
                refund_status,
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouterData<RSync> {
    type Error = Error;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::from(item.response.status);
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id,
                refund_status,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct ErrorResponse {
    pub error: ApiErrorResponse,
}

#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq, Serialize)]
pub struct ApiErrorResponse {
    pub code: Option<String>,
    pub message: String,
}

pub fn is_transaction_event(event: &Shift4WebhookEvent) -> bool {
    matches!(
        event,
        Shift4WebhookEvent::ChargeCaptured
            | Shift4WebhookEvent::ChargeFailed
            | Shift4WebhookEvent::ChargeSucceeded
            | Shift4WebhookEvent::ChargeUpdated
    )
}

pub fn is_refund_event(event: &Shift4WebhookEvent) -> bool {
    matches!(event, Shift4WebhookEvent::ChargeRefunded)
}

impl From<Shift4WebhookEvent> for IncomingWebhookEvent {
    fn from(event: Shift4WebhookEvent) -> Self {
        match event {
            Shift4WebhookEvent::ChargeSucceeded | Shift4WebhookEvent::ChargeUpdated => {
                //reference : https://dev.shift4.com/docs/api#event-types
                Self::PaymentIntentProcessing
            }
            Shift4WebhookEvent::ChargeCaptured => Self::PaymentIntentSuccess,
            Shift4WebhookEvent::ChargeFailed => Self::PaymentIntentFailure,
            Shift4WebhookEvent::ChargeRefunded => Self::RefundSuccess,
            Shift4WebhookEvent::Unknown => Self::EventNotSupported,
        }
    }
}


// File: crates/hyperswitch_interfaces/src/api.rs
// Module: hyperswitch_interfaces

//! API interface

/// authentication module
pub mod authentication;
/// authentication_v2 module
pub mod authentication_v2;
pub mod disputes;
pub mod disputes_v2;
pub mod files;
pub mod files_v2;
#[cfg(feature = "frm")]
pub mod fraud_check;
#[cfg(feature = "frm")]
pub mod fraud_check_v2;
pub mod payments;
pub mod payments_v2;
#[cfg(feature = "payouts")]
pub mod payouts;
#[cfg(feature = "payouts")]
pub mod payouts_v2;
pub mod refunds;
pub mod refunds_v2;
pub mod revenue_recovery;
pub mod revenue_recovery_v2;
pub mod subscriptions;
pub mod subscriptions_v2;
pub mod vault;
pub mod vault_v2;

use std::fmt::Debug;

use common_enums::{
    enums::{CallConnectorAction, CaptureMethod, EventClass, PaymentAction, PaymentMethodType},
    PaymentMethod,
};
use common_utils::{
    errors::CustomResult,
    request::{Method, Request, RequestContent},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    connector_endpoints::Connectors,
    errors::api_error_response::ApiErrorResponse,
    payment_method_data::PaymentMethodData,
    router_data::{
        AccessToken, AccessTokenAuthenticationResponse, ConnectorAuthType, ErrorResponse,
        RouterData,
    },
    router_data_v2::{
        flow_common_types::{AuthenticationTokenFlowData, WebhookSourceVerifyData},
        AccessTokenFlowData, MandateRevokeFlowData, UasFlowData,
    },
    router_flow_types::{
        mandate_revoke::MandateRevoke, AccessTokenAuth, AccessTokenAuthentication, Authenticate,
        AuthenticationConfirmation, PostAuthenticate, PreAuthenticate, VerifyWebhookSource,
    },
    router_request_types::{
        unified_authentication_service::{
            UasAuthenticationRequestData, UasAuthenticationResponseData,
            UasConfirmationRequestData, UasPostAuthenticationRequestData,
            UasPreAuthenticationRequestData,
        },
        AccessTokenAuthenticationRequestData, AccessTokenRequestData, MandateRevokeRequestData,
        VerifyWebhookSourceRequestData,
    },
    router_response_types::{
        ConnectorInfo, MandateRevokeResponseData, PaymentMethodDetails, SupportedPaymentMethods,
        VerifyWebhookSourceResponseData,
    },
};
use masking::Maskable;
use serde_json::json;

#[cfg(feature = "frm")]
pub use self::fraud_check::*;
#[cfg(feature = "frm")]
pub use self::fraud_check_v2::*;
#[cfg(feature = "payouts")]
pub use self::payouts::*;
#[cfg(feature = "payouts")]
pub use self::payouts_v2::*;
pub use self::{payments::*, refunds::*, vault::*, vault_v2::*};
use crate::{
    api::subscriptions::Subscriptions, connector_integration_v2::ConnectorIntegrationV2, consts,
    errors, events::connector_api_logs::ConnectorEvent, metrics, types, webhooks,
};

/// Connector trait
pub trait Connector:
    Send
    + Refund
    + Payment
    + ConnectorRedirectResponse
    + webhooks::IncomingWebhook
    + ConnectorAccessToken
    + ConnectorAuthenticationToken
    + disputes::Dispute
    + files::FileUpload
    + ConnectorTransactionId
    + Payouts
    + ConnectorVerifyWebhookSource
    + FraudCheck
    + ConnectorMandateRevoke
    + authentication::ExternalAuthentication
    + TaxCalculation
    + UnifiedAuthenticationService
    + revenue_recovery::RevenueRecovery
    + ExternalVault
    + Subscriptions
{
}

impl<
        T: Refund
            + Payment
            + ConnectorRedirectResponse
            + Send
            + webhooks::IncomingWebhook
            + ConnectorAccessToken
            + ConnectorAuthenticationToken
            + disputes::Dispute
            + files::FileUpload
            + ConnectorTransactionId
            + Payouts
            + ConnectorVerifyWebhookSource
            + FraudCheck
            + ConnectorMandateRevoke
            + authentication::ExternalAuthentication
            + TaxCalculation
            + UnifiedAuthenticationService
            + revenue_recovery::RevenueRecovery
            + ExternalVault
            + Subscriptions,
    > Connector for T
{
}

/// Alias for Box<&'static (dyn Connector + Sync)>
pub type BoxedConnector = Box<&'static (dyn Connector + Sync)>;

/// type BoxedConnectorIntegration
pub type BoxedConnectorIntegration<'a, T, Req, Resp> =
    Box<&'a (dyn ConnectorIntegration<T, Req, Resp> + Send + Sync)>;

/// trait ConnectorIntegrationAny
pub trait ConnectorIntegrationAny<T, Req, Resp>: Send + Sync + 'static {
    /// fn get_connector_integration
    fn get_connector_integration(&self) -> BoxedConnectorIntegration<'_, T, Req, Resp>;
}

impl<S, T, Req, Resp> ConnectorIntegrationAny<T, Req, Resp> for S
where
    S: ConnectorIntegration<T, Req, Resp> + Send + Sync,
{
    fn get_connector_integration(&self) -> BoxedConnectorIntegration<'_, T, Req, Resp> {
        Box::new(self)
    }
}

/// trait ConnectorIntegration
pub trait ConnectorIntegration<T, Req, Resp>:
    ConnectorIntegrationAny<T, Req, Resp> + Sync + ConnectorCommon
{
    /// fn get_headers
    fn get_headers(
        &self,
        _req: &RouterData<T, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        Ok(vec![])
    }

    /// fn get_content_type
    fn get_content_type(&self) -> &'static str {
        mime::APPLICATION_JSON.essence_str()
    }

    /// fn get_content_type
    fn get_accept_type(&self) -> &'static str {
        mime::APPLICATION_JSON.essence_str()
    }

    /// primarily used when creating signature based on request method of payment flow
    fn get_http_method(&self) -> Method {
        Method::Post
    }

    /// fn get_url
    fn get_url(
        &self,
        _req: &RouterData<T, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(String::new())
    }

    /// fn get_request_body
    fn get_request_body(
        &self,
        _req: &RouterData<T, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Ok(RequestContent::Json(Box::new(json!(r#"{}"#))))
    }

    /// fn get_request_form_data
    fn get_request_form_data(
        &self,
        _req: &RouterData<T, Req, Resp>,
    ) -> CustomResult<Option<reqwest::multipart::Form>, errors::ConnectorError> {
        Ok(None)
    }

    /// fn build_request
    fn build_request(
        &self,
        req: &RouterData<T, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        metrics::UNIMPLEMENTED_FLOW.add(
            1,
            router_env::metric_attributes!(("connector", req.connector.clone())),
        );
        Ok(None)
    }

    /// fn handle_response
    fn handle_response(
        &self,
        data: &RouterData<T, Req, Resp>,
        event_builder: Option<&mut ConnectorEvent>,
        _res: types::Response,
    ) -> CustomResult<RouterData<T, Req, Resp>, errors::ConnectorError>
    where
        T: Clone,
        Req: Clone,
        Resp: Clone,
    {
        event_builder.map(|e| e.set_error(json!({"error": "Not Implemented"})));
        Ok(data.clone())
    }

    /// fn get_error_response
    fn get_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        event_builder.map(|event| event.set_error(json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        Ok(ErrorResponse::get_not_implemented())
    }

    /// fn get_5xx_error_response
    fn get_5xx_error_response(
        &self,
        res: types::Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        event_builder.map(|event| event.set_error(json!({"error": res.response.escape_ascii().to_string(), "status_code": res.status_code})));
        let error_message = match res.status_code {
            500 => "internal_server_error",
            501 => "not_implemented",
            502 => "bad_gateway",
            503 => "service_unavailable",
            504 => "gateway_timeout",
            505 => "http_version_not_supported",
            506 => "variant_also_negotiates",
            507 => "insufficient_storage",
            508 => "loop_detected",
            510 => "not_extended",
            511 => "network_authentication_required",
            _ => "unknown_error",
        };
        Ok(ErrorResponse {
            code: res.status_code.to_string(),
            message: error_message.to_string(),
            reason: String::from_utf8(res.response.to_vec()).ok(),
            status_code: res.status_code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }

    /// whenever capture sync is implemented at the connector side, this method should be overridden
    fn get_multiple_capture_sync_method(
        &self,
    ) -> CustomResult<CaptureSyncMethod, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("multiple capture sync".into()).into())
    }

    /// fn get_certificate
    fn get_certificate(
        &self,
        _req: &RouterData<T, Req, Resp>,
    ) -> CustomResult<Option<String>, errors::ConnectorError> {
        Ok(None)
    }

    /// fn get_certificate_key
    fn get_certificate_key(
        &self,
        _req: &RouterData<T, Req, Resp>,
    ) -> CustomResult<Option<String>, errors::ConnectorError> {
        Ok(None)
    }
}

/// Sync Methods for multiple captures
#[derive(Debug)]
pub enum CaptureSyncMethod {
    /// For syncing multiple captures individually
    Individual,
    /// For syncing multiple captures together
    Bulk,
}

/// Connector accepted currency unit as either "Base" or "Minor"
#[derive(Debug)]
pub enum CurrencyUnit {
    /// Base currency unit
    Base,
    /// Minor currency unit
    Minor,
}

/// The trait that provides the common
pub trait ConnectorCommon {
    /// Name of the connector (in lowercase).
    fn id(&self) -> &'static str;

    /// Connector accepted currency unit as either "Base" or "Minor"
    fn get_currency_unit(&self) -> CurrencyUnit {
        CurrencyUnit::Minor // Default implementation should be remove once it is implemented in all connectors
    }

    /// HTTP header used for authorization.
    fn get_auth_header(
        &self,
        _auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        Ok(Vec::new())
    }

    /// HTTP `Content-Type` to be used for POST requests.
    /// Defaults to `application/json`.
    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    // FIXME write doc - think about this
    // fn headers(&self) -> Vec<(&str, &str)>;

    /// The base URL for interacting with the connector's API.
    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str;

    /// common error response for a connector if it is same in all case
    fn build_error_response(
        &self,
        res: types::Response,
        _event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        Ok(ErrorResponse {
            status_code: res.status_code,
            code: consts::NO_ERROR_CODE.to_string(),
            message: consts::NO_ERROR_MESSAGE.to_string(),
            reason: None,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

/// The trait that provides specifications about the connector
pub trait ConnectorSpecifications {
    /// Details related to payment method supported by the connector
    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        None
    }

    /// Supported webhooks flows
    fn get_supported_webhook_flows(&self) -> Option<&'static [EventClass]> {
        None
    }

    /// About the connector
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        None
    }

    /// Check if connector should make another request to create an access token
    /// Connectors should override this method if they require an authentication token to create a new access token
    fn authentication_token_for_token_creation(&self) -> bool {
        false
    }

    /// Check if connector should make another request to create an customer
    /// Connectors should override this method if they require to create a connector customer
    fn should_call_connector_customer(
        &self,
        _payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> bool {
        false
    }

    #[cfg(not(feature = "v2"))]
    /// Generate connector request reference ID
    fn generate_connector_request_reference_id(
        &self,
        _payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
        is_config_enabled_to_send_payment_id_as_connector_request_id: bool,
    ) -> String {
        // Send payment_id if config is enabled for a merchant, else send attempt_id
        if is_config_enabled_to_send_payment_id_as_connector_request_id {
            payment_attempt.payment_id.get_string_repr().to_owned()
        } else {
            payment_attempt.attempt_id.to_owned()
        }
    }

    #[cfg(feature = "v2")]
    /// Generate connector request reference ID
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> String {
        payment_intent
            .merchant_reference_id
            .as_ref()
            .map(|id| id.get_string_repr().to_owned())
            .unwrap_or_else(|| payment_attempt.id.get_string_repr().to_owned())
    }
}

/// Extended trait for connector common to allow functions with generic type
pub trait ConnectorCommonExt<Flow, Req, Resp>:
    ConnectorCommon + ConnectorIntegration<Flow, Req, Resp>
{
    /// common header builder when every request for the connector have same headers
    fn build_headers(
        &self,
        _req: &RouterData<Flow, Req, Resp>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        Ok(Vec::new())
    }
}

/// trait ConnectorMandateRevoke
pub trait ConnectorMandateRevoke:
    ConnectorIntegration<MandateRevoke, MandateRevokeRequestData, MandateRevokeResponseData>
{
}

/// trait ConnectorMandateRevokeV2
pub trait ConnectorMandateRevokeV2:
    ConnectorIntegrationV2<
    MandateRevoke,
    MandateRevokeFlowData,
    MandateRevokeRequestData,
    MandateRevokeResponseData,
>
{
}

/// trait ConnectorAuthenticationToken
pub trait ConnectorAuthenticationToken:
    ConnectorIntegration<
    AccessTokenAuthentication,
    AccessTokenAuthenticationRequestData,
    AccessTokenAuthenticationResponse,
>
{
}

/// trait ConnectorAuthenticationTokenV2
pub trait ConnectorAuthenticationTokenV2:
    ConnectorIntegrationV2<
    AccessTokenAuthentication,
    AuthenticationTokenFlowData,
    AccessTokenAuthenticationRequestData,
    AccessTokenAuthenticationResponse,
>
{
}

/// trait ConnectorAccessToken
pub trait ConnectorAccessToken:
    ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken>
{
}

/// trait ConnectorAccessTokenV2
pub trait ConnectorAccessTokenV2:
    ConnectorIntegrationV2<AccessTokenAuth, AccessTokenFlowData, AccessTokenRequestData, AccessToken>
{
}

/// trait ConnectorVerifyWebhookSource
pub trait ConnectorVerifyWebhookSource:
    ConnectorIntegration<
    VerifyWebhookSource,
    VerifyWebhookSourceRequestData,
    VerifyWebhookSourceResponseData,
>
{
}

/// trait ConnectorVerifyWebhookSourceV2
pub trait ConnectorVerifyWebhookSourceV2:
    ConnectorIntegrationV2<
    VerifyWebhookSource,
    WebhookSourceVerifyData,
    VerifyWebhookSourceRequestData,
    VerifyWebhookSourceResponseData,
>
{
}

/// trait UnifiedAuthenticationService
pub trait UnifiedAuthenticationService:
    ConnectorCommon
    + UasPreAuthentication
    + UasPostAuthentication
    + UasAuthenticationConfirmation
    + UasAuthentication
{
}

/// trait UasPreAuthentication
pub trait UasPreAuthentication:
    ConnectorIntegration<
    PreAuthenticate,
    UasPreAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasPostAuthentication
pub trait UasPostAuthentication:
    ConnectorIntegration<
    PostAuthenticate,
    UasPostAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasAuthenticationConfirmation
pub trait UasAuthenticationConfirmation:
    ConnectorIntegration<
    AuthenticationConfirmation,
    UasConfirmationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasAuthentication
pub trait UasAuthentication:
    ConnectorIntegration<Authenticate, UasAuthenticationRequestData, UasAuthenticationResponseData>
{
}

/// trait UnifiedAuthenticationServiceV2
pub trait UnifiedAuthenticationServiceV2:
    ConnectorCommon
    + UasPreAuthenticationV2
    + UasPostAuthenticationV2
    + UasAuthenticationV2
    + UasAuthenticationConfirmationV2
{
}

///trait UasPreAuthenticationV2
pub trait UasPreAuthenticationV2:
    ConnectorIntegrationV2<
    PreAuthenticate,
    UasFlowData,
    UasPreAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasPostAuthenticationV2
pub trait UasPostAuthenticationV2:
    ConnectorIntegrationV2<
    PostAuthenticate,
    UasFlowData,
    UasPostAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasAuthenticationConfirmationV2
pub trait UasAuthenticationConfirmationV2:
    ConnectorIntegrationV2<
    AuthenticationConfirmation,
    UasFlowData,
    UasConfirmationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait UasAuthenticationV2
pub trait UasAuthenticationV2:
    ConnectorIntegrationV2<
    Authenticate,
    UasFlowData,
    UasAuthenticationRequestData,
    UasAuthenticationResponseData,
>
{
}

/// trait ConnectorValidation
pub trait ConnectorValidation: ConnectorCommon + ConnectorSpecifications {
    /// Validate, the payment request against the connector supported features
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<CaptureMethod>,
        payment_method: PaymentMethod,
        pmt: Option<PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();
        let is_default_capture_method =
            [CaptureMethod::Automatic, CaptureMethod::SequentialAutomatic]
                .contains(&capture_method);
        let is_feature_supported = match self.get_supported_payment_methods() {
            Some(supported_payment_methods) => {
                let connector_payment_method_type_info = get_connector_payment_method_type_info(
                    supported_payment_methods,
                    payment_method,
                    pmt,
                    self.id(),
                )?;

                connector_payment_method_type_info
                    .map(|payment_method_type_info| {
                        payment_method_type_info
                            .supported_capture_methods
                            .contains(&capture_method)
                    })
                    .unwrap_or(true)
            }
            None => is_default_capture_method,
        };

        if is_feature_supported {
            Ok(())
        } else {
            Err(errors::ConnectorError::NotSupported {
                message: capture_method.to_string(),
                connector: self.id(),
            }
            .into())
        }
    }

    /// fn validate_mandate_payment
    fn validate_mandate_payment(
        &self,
        pm_type: Option<PaymentMethodType>,
        _pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        let connector = self.id();
        match pm_type {
            Some(pm_type) => Err(errors::ConnectorError::NotSupported {
                message: format!("{pm_type} mandate payment"),
                connector,
            }
            .into()),
            None => Err(errors::ConnectorError::NotSupported {
                message: " mandate payment".to_string(),
                connector,
            }
            .into()),
        }
    }

    /// fn validate_psync_reference_id
    fn validate_psync_reference_id(
        &self,
        data: &hyperswitch_domain_models::router_request_types::PaymentsSyncData,
        _is_three_ds: bool,
        _status: common_enums::enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        data.connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)
            .map(|_| ())
    }

    /// fn is_webhook_source_verification_mandatory
    fn is_webhook_source_verification_mandatory(&self) -> bool {
        false
    }
}

/// trait ConnectorRedirectResponse
pub trait ConnectorRedirectResponse {
    /// fn get_flow_type
    fn get_flow_type(
        &self,
        _query_params: &str,
        _json_payload: Option<serde_json::Value>,
        _action: PaymentAction,
    ) -> CustomResult<CallConnectorAction, errors::ConnectorError> {
        Ok(CallConnectorAction::Avoid)
    }
}

/// Empty trait for when payouts feature is disabled
#[cfg(not(feature = "payouts"))]
pub trait Payouts {}
/// Empty trait for when payouts feature is disabled
#[cfg(not(feature = "payouts"))]
pub trait PayoutsV2 {}

/// Empty trait for when frm feature is disabled
#[cfg(not(feature = "frm"))]
pub trait FraudCheck {}
/// Empty trait for when frm feature is disabled
#[cfg(not(feature = "frm"))]
pub trait FraudCheckV2 {}

fn get_connector_payment_method_type_info(
    supported_payment_method: &SupportedPaymentMethods,
    payment_method: PaymentMethod,
    payment_method_type: Option<PaymentMethodType>,
    connector: &'static str,
) -> CustomResult<Option<PaymentMethodDetails>, errors::ConnectorError> {
    let payment_method_details =
        supported_payment_method
            .get(&payment_method)
            .ok_or_else(|| errors::ConnectorError::NotSupported {
                message: payment_method.to_string(),
                connector,
            })?;

    payment_method_type
        .map(|pmt| {
            payment_method_details.get(&pmt).cloned().ok_or_else(|| {
                errors::ConnectorError::NotSupported {
                    message: format!("{payment_method} {pmt}"),
                    connector,
                }
                .into()
            })
        })
        .transpose()
}

/// ConnectorTransactionId trait
pub trait ConnectorTransactionId: ConnectorCommon + Sync {
    /// fn connector_transaction_id
    fn connector_transaction_id(
        &self,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> Result<Option<String>, ApiErrorResponse> {
        Ok(payment_attempt
            .get_connector_payment_id()
            .map(ToString::to_string))
    }
}


// Struct: RoutingLinkWrapper
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 0

pub struct RoutingLinkWrapper


// Struct: Details
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Details


// Struct: ExternalTokenResponse
// File: crates/api_models/src/external_service_auth.rs
// Module: api_models
// Implementations: 0

pub struct ExternalTokenResponse


// Struct: Balances
// File: crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Balances