pub mod transformers;

use std::{any::type_name, borrow::Cow, collections::HashMap, fmt::Debug};

use bytes::Bytes;
use common_enums::CurrencyUnit;
use common_utils::{
    consts,
    crypto::{self, GenerateDigest, VerifySignature},
    errors::CustomResult,
    events,
    ext_traits::{ByteSliceExt, BytesExt},
    types::StringMajorUnit,
};
use domain_types::{
    connector_flow::{Authorize, Capture, PSync, RSync, Refund, RepeatPayment, SetupMandate, Void},
    connector_types::{
        ConnectorSpecifications, ConnectorWebhookSecrets, EventContext, EventType,
        MandateReferenceId, PaymentFlowData, PaymentVoidData, PaymentWebhookReference,
        PaymentsAuthorizeData, PaymentsCaptureData, PaymentsResponseData, PaymentsSyncData,
        RefundFlowData, RefundSyncData, RefundWebhookDetailsResponse, RefundWebhookReference,
        RefundsData, RefundsResponseData, RepeatPaymentData, RequestDetails,
        SetupMandateRequestData, WebhookDetailsResponse, WebhookResourceReference,
    },
    payment_method_data::PaymentMethodDataTypes,
    router_data::{ConnectorSpecificConfig, ErrorResponse},
    router_data_v2::RouterDataV2,
    router_response_types::Response,
    types::Connectors,
    utils,
};
use error_stack::{report, ResultExt};
use hyperswitch_masking::{ExposeInterface, Maskable, PeekInterface, Secret};
use interfaces::{
    api::ConnectorCommon,
    connector_integration_v2::ConnectorIntegrationV2,
    connector_types::{self},
    decode::BodyDecoding,
    verification::SourceVerification,
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing::{error, info, warn};
use transformers::{
    self as fiuu, FiuuPaymentCancelRequest, FiuuPaymentCancelResponse, FiuuPaymentRequest,
    FiuuPaymentResponse, FiuuPaymentSyncRequest, FiuuPaymentsRequest as FiuuRepeatPaymentsRequest,
    FiuuPaymentsResponse, FiuuPaymentsResponse as FiuuRepeatPaymentsResponse,
    FiuuPaymentsResponse as FiuuSetupMandateResponse, FiuuRefundRequest, FiuuRefundResponse,
    FiuuRefundSyncRequest, FiuuRefundSyncResponse, FiuuSetupMandateRequest, FiuuWebhooksResponse,
    PaymentCaptureRequest, PaymentCaptureResponse,
};

use super::macros;
use crate::{
    types::ResponseRouterData, utils::xml_utils::flatten_json_structure, with_error_response_body,
    with_response_body,
};
use domain_types::errors::ConnectorError;
use domain_types::errors::{IntegrationError, WebhookError};

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::ConnectorServiceTrait<T> for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentAuthorizeV2<T> for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentSyncV2 for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentVoidV2 for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::RefundSyncV2 for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::RefundV2 for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::PaymentCapture for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::ValidationTrait for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::VerifyRedirectResponse for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize> SourceVerification
    for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize> BodyDecoding
    for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::RepeatPaymentV2<T> for Fiuu<T>
{
}
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::SetupMandateV2<T> for Fiuu<T>
{
}
// Authentication trait implementations
macros::macro_connector_payout_implementation!(
    connector: Fiuu,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize]
);

macros::create_all_prerequisites!(
    connector_name: Fiuu,
    generic_type: T,
    api: [
        (
            flow: Authorize,
            request_body: FiuuPaymentRequest<T>,
            response_body: FiuuPaymentsResponse,
            router_data: RouterDataV2<Authorize, PaymentFlowData, PaymentsAuthorizeData<T>, PaymentsResponseData>,
        ),
        (
            flow: PSync,
            request_body: FiuuPaymentSyncRequest,
            response_body: FiuuPaymentResponse,
            router_data: RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
        ),
        (
            flow: Capture,
            request_body: PaymentCaptureRequest,
            response_body: PaymentCaptureResponse,
            router_data: RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>,
        ),
        (
            flow: Void,
            request_body: FiuuPaymentCancelRequest,
            response_body: FiuuPaymentCancelResponse,
            router_data: RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>,
        ),
        (
            flow: Refund,
            request_body: FiuuRefundRequest,
            response_body: FiuuRefundResponse,
            router_data: RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>,
        ),
        (
            flow: RSync,
            request_body: FiuuRefundSyncRequest,
            response_body: FiuuRefundSyncResponse,
            router_data: RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>,
        ),
        (
            flow: RepeatPayment,
            request_body: FiuuRepeatPaymentsRequest<T>,
            response_body: FiuuRepeatPaymentsResponse,
            router_data: RouterDataV2<RepeatPayment, PaymentFlowData, RepeatPaymentData<T>, PaymentsResponseData>,
        ),
        (
            flow: SetupMandate,
            request_body: FiuuSetupMandateRequest<T>,
            response_body: FiuuSetupMandateResponse,
            router_data: RouterDataV2<SetupMandate, PaymentFlowData, SetupMandateRequestData<T>, PaymentsResponseData>,
        )
    ],
    amount_converters: [
        amount_converter: StringMajorUnit
    ],
    member_functions: {
        pub fn preprocess_response_bytes<F, FCD, Req, Res>(
            &self,
            _req: &RouterDataV2<F, FCD, Req, Res>,
            response_bytes: Bytes,
            status_code: u16,
        ) -> Result<Bytes, ConnectorError> {
                let response_str = String::from_utf8(response_bytes.to_vec()).map_err(|e| {
                error!("Error in Deserializing Response Data: {:?}", e);
                crate::utils::response_deserialization_fail(status_code, "fiuu: response body did not match the expected format; confirm API version and connector documentation.")
            })?;

            let mut json = serde_json::Map::new();
            let mut miscellaneous: HashMap<String, Secret<String>> = HashMap::new();

            for line in response_str.lines() {
                if let Some((key, value)) = line.split_once('=') {
                    if key.trim().is_empty() {
                        error!("Null or empty key encountered in response.");
                        continue;
                    }

                    if let Some(old_value) = json.insert(key.to_string(), Value::String(value.to_string()))
                    {
                        warn!("Repeated key encountered: {}", key);
                        miscellaneous.insert(key.to_string(), Secret::new(old_value.to_string()));
                    }
                }
            }
            if !miscellaneous.is_empty() {
                let misc_value = serde_json::to_value(miscellaneous).map_err(|e| {
                    error!("Error serializing miscellaneous data: {:?}", e);
                    crate::utils::response_deserialization_fail(status_code, "fiuu: response body did not match the expected format; confirm API version and connector documentation.")
                })?;
                json.insert("miscellaneous".to_string(), misc_value);
            }
                // Extract and flatten the JSON structure
            let flattened_json = flatten_json_structure(Value::Object(json));

            // Convert JSON Value to string and then to bytes
            let json_string = serde_json::to_string(&flattened_json).map_err(|e| {
                tracing::error!(error=?e, "Failed to convert to JSON string");
                crate::utils::response_deserialization_fail(status_code, "fiuu: response body did not match the expected format; confirm API version and connector documentation.")
            })?;

            tracing::info!(json=?json_string, "Flattened JSON structure");

            // Return JSON as bytes
            Ok(Bytes::from(json_string.into_bytes()))
        }

        pub fn build_headers<F, FCD, Req, Res>(
            &self,
            _req: &RouterDataV2<F, FCD, Req, Res>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError>
        where
            Self: ConnectorIntegrationV2<F, FCD, Req, Res>,
        {
            Ok(vec![])
        }

        pub fn connector_base_url_payments<'a, F, Req, Res>(
            &self,
            req: &'a RouterDataV2<F, PaymentFlowData, Req, Res>,
        ) -> &'a str {
            &req.resource_common_data.connectors.fiuu.base_url
        }

        pub fn connector_secondary_base_url_payments<'a, F, Req, Res>(
            &'a self,
            req: &'a RouterDataV2<F, PaymentFlowData, Req, Res>,
        ) -> CustomResult<&'a str, IntegrationError> {
            let base_url = req
                .resource_common_data
                .connectors
                .fiuu
                .secondary_base_url
                .as_deref()
                .ok_or(IntegrationError::InvalidConnectorConfig {
                    config: "secondary_base_url",
                    context: Default::default()
})?;

            Ok(base_url)
        }

        pub fn connector_base_url_refunds<'a, F, Req, Res>(
            &self,
            req: &'a RouterDataV2<F, RefundFlowData, Req, Res>,
        ) -> CustomResult<&'a str, IntegrationError> {
            let base_url = req
                .resource_common_data
                .connectors
                .fiuu
                .secondary_base_url
                .as_deref()
                .ok_or(IntegrationError::InvalidConnectorConfig {
                    config: "secondary_base_url",
                    context: Default::default()
})?;

            Ok(base_url)
        }
    }
);

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize> ConnectorCommon
    for Fiuu<T>
{
    fn id(&self) -> &'static str {
        "fiuu"
    }

    fn get_currency_unit(&self) -> CurrencyUnit {
        CurrencyUnit::Base
    }

    fn common_get_content_type(&self) -> &'static str {
        "multipart/form-data"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.fiuu.base_url.as_ref()
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut events::Event>,
        _connector_config: &ConnectorSpecificConfig,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        if res.response.is_empty() {
            return 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,
            });
        }

        let response: Result<
            fiuu::FiuuErrorResponse,
            error_stack::Report<common_utils::errors::ParsingError>,
        > = res.response.parse_struct("FiuuErrorResponse");

        match response {
            Ok(response) => {
                with_error_response_body!(event_builder, response);

                Ok(ErrorResponse {
                    status_code: res.status_code,
                    code: response.error_code.clone(),
                    message: response.error_desc.clone(),
                    reason: Some(response.error_desc.clone()),
                    attempt_status: None,
                    connector_transaction_id: None,
                    network_advice_code: None,
                    network_decline_code: None,
                    network_error_message: None,
                })
            }
            Err(error_msg) => {
                if let Some(event) = event_builder {
                    event.set_connector_response(&serde_json::json!({
                        "error": "Error response parsing failed",
                        "status_code": res.status_code
                    }));
                }
                error!(deserialization_error =? error_msg);
                crate::utils::handle_json_response_deserialization_failure(res, "fiuu")
            }
        }
    }
}

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Fiuu,
    curl_request: FormData(FiuuPaymentRequest<T>),
    curl_response: FiuuPaymentsResponse,
    flow_name: Authorize,
    resource_common_data: PaymentFlowData,
    flow_request: PaymentsAuthorizeData<T>,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<Authorize, PaymentFlowData, PaymentsAuthorizeData<T>, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            self.build_headers(req)
        }
        fn get_url(
            &self,
            req: &RouterDataV2<Authorize, PaymentFlowData, PaymentsAuthorizeData<T>, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!(
                "{}RMS/API/Direct/1.4.0/index.php",
                self.connector_base_url_payments(req)
            ))
        }
    }
);

// SetupMandate (Pay.SetupRecurring) — Fiuu has no dedicated vault endpoint.
// Tokenizing a card for off-session reuse goes through the same
// `RMS/API/Direct/1.4.0/index.php` Direct API that Authorize uses, with
// `mpstokenstatus=1` set on the card payload. The response carries an
// `extraP.token` field which the shared `FiuuPaymentsResponse` transformer
// lifts onto `MandateReference.connector_mandate_id`; RepeatPayment then
// replays that id via its existing `RMS/API/Recurring/input_v7.php` flow.
macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Fiuu,
    curl_request: FormData(FiuuSetupMandateRequest<T>),
    curl_response: FiuuSetupMandateResponse,
    flow_name: SetupMandate,
    resource_common_data: PaymentFlowData,
    flow_request: SetupMandateRequestData<T>,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<SetupMandate, PaymentFlowData, SetupMandateRequestData<T>, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            self.build_headers(req)
        }
        fn get_url(
            &self,
            req: &RouterDataV2<SetupMandate, PaymentFlowData, SetupMandateRequestData<T>, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!(
                "{}RMS/API/Direct/1.4.0/index.php",
                self.connector_base_url_payments(req)
            ))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Fiuu,
    curl_request: FormData(FiuuRepeatPaymentsRequest<T>),
    curl_response: FiuuRepeatPaymentsResponse,
    flow_name: RepeatPayment,
    resource_common_data: PaymentFlowData,
    flow_request: RepeatPaymentData<T>,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<RepeatPayment, PaymentFlowData, RepeatPaymentData<T>, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            self.build_headers(req)
        }
        fn get_url(
            &self,
            req: &RouterDataV2<RepeatPayment, PaymentFlowData, RepeatPaymentData<T>, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            let url = match req.request.mandate_reference {
                MandateReferenceId::ConnectorMandateId(_) =>{
                    format!(
                        "{}/RMS/API/Recurring/input_v7.php",
                        self.connector_base_url_payments(req)
                    )
                }
                MandateReferenceId::NetworkMandateId(_)
                | MandateReferenceId::NetworkTokenWithNTI(_) => {
                    format!(
                        "{}RMS/API/Direct/1.4.0/index.php",
                        self.connector_base_url_payments(req)
                    )
                }
            };
            Ok(url)
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Fiuu,
    curl_request: FormData(PaymentCaptureRequest),
    curl_response: PaymentCaptureResponse,
    flow_name: Capture,
    resource_common_data: PaymentFlowData,
    flow_request: PaymentsCaptureData,
    flow_response: PaymentsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            self.build_headers(req)
        }
        fn get_url(
            &self,
            req: &RouterDataV2<Capture, PaymentFlowData, PaymentsCaptureData, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!(
                "{}RMS/API/capstxn/index.php",
                self.connector_secondary_base_url_payments(req)?
            ))
        }
    }
);

// Add implementation for Void
macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Fiuu,
    curl_request: FormData(FiuuPaymentCancelRequest),
    curl_response: FiuuPaymentCancelResponse,
    flow_name: Void,
    resource_common_data: PaymentFlowData,
    flow_request: PaymentVoidData,
    flow_response: PaymentsResponseData,
    http_method: Post,
    preprocess_response: true,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            self.build_headers(req)
        }
        fn get_url(
            &self,
            req: &RouterDataV2<Void, PaymentFlowData, PaymentVoidData, PaymentsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!(
                "{}RMS/API/refundAPI/refund.php",
                self.connector_secondary_base_url_payments(req)?
            ))
        }
    }
);

// Add implementation for Refund
macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Fiuu,
    curl_request: FormData(FiuuRefundRequest),
    curl_response: FiuuRefundResponse,
    flow_name: Refund,
    resource_common_data: RefundFlowData,
    flow_request: RefundsData,
    flow_response: RefundsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            self.build_headers(req)
        }
        fn get_url(
            &self,
            req: &RouterDataV2<Refund, RefundFlowData, RefundsData, RefundsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!(
                "{}RMS/API/refundAPI/index.php",
                self.connector_base_url_refunds(req)?
            ))
        }
    }
);

macros::macro_connector_implementation!(
    connector_default_implementations: [get_content_type, get_error_response_v2],
    connector: Fiuu,
    curl_request: FormData(FiuuRefundSyncRequest),
    curl_response: FiuuRefundSyncResponse,
    flow_name: RSync,
    resource_common_data: RefundFlowData,
    flow_request: RefundSyncData,
    flow_response: RefundsResponseData,
    http_method: Post,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    other_functions: {
        fn get_headers(
            &self,
            req: &RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>,
        ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
            self.build_headers(req)
        }
        fn get_url(
            &self,
            req: &RouterDataV2<RSync, RefundFlowData, RefundSyncData, RefundsResponseData>,
        ) -> CustomResult<String, IntegrationError> {
            Ok(format!(
                "{}RMS/API/refundAPI/q_by_txn.php",
                self.connector_base_url_refunds(req)?
            ))
        }
    }
);

// PSync is not implemented using the macro structure because the response is parsed differently according to the header
impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    ConnectorIntegrationV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>
    for Fiuu<T>
{
    fn get_headers(
        &self,
        req: &RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, IntegrationError> {
        self.build_headers(req)
    }

    fn get_url(
        &self,
        req: &RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
    ) -> CustomResult<String, IntegrationError> {
        Ok(format!(
            "{}RMS/API/gate-query/index.php",
            self.connector_secondary_base_url_payments(req)?
        ))
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_request_body(
        &self,
        req: &RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
    ) -> CustomResult<Option<macro_types::RequestContent>, IntegrationError> {
        let bridge = self.p_sync;
        let input_data = FiuuRouterData {
            connector: self.to_owned(),
            router_data: req.clone(),
        };
        let request = bridge.request_body(input_data)?;
        let form_data = <FiuuPaymentSyncRequest as GetFormData>::get_form_data(&request);
        Ok(Some(macro_types::RequestContent::FormData(form_data)))
    }

    fn handle_response_v2(
        &self,
        data: &RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
        event_builder: Option<&mut events::Event>,
        res: Response,
    ) -> CustomResult<
        RouterDataV2<PSync, PaymentFlowData, PaymentsSyncData, PaymentsResponseData>,
        ConnectorError,
    > {
        match res.headers {
            Some(headers) => {
                let content_header = utils::get_http_header("Content-type", &headers)
                    .attach_printable("Missing content type in headers")
                    .change_context(crate::utils::response_handling_fail_for_connector(
                        res.status_code,
                        "fiuu",
                    ))?;
                let response: FiuuPaymentResponse = if content_header
                    .to_lowercase()
                    .replace(' ', "")
                    == "text/plain;charset=utf-8"
                {
                    parse_response(&res.response, res.status_code)
                } else {
                    Err(error_stack::Report::from(crate::utils::response_deserialization_fail(res.status_code, "fiuu: response body did not match the expected format; confirm API version and connector documentation.")))
                        .attach_printable(format!("Expected content type to be text/plain;charset=UTF-8 , but received different content type as {content_header} in response"))?
                }?;
                with_response_body!(event_builder, response);

                RouterDataV2::try_from(ResponseRouterData {
                    response,
                    router_data: data.clone(),
                    http_code: res.status_code,
                })
                .change_context(
                    crate::utils::response_handling_fail_for_connector(res.status_code, "fiuu"),
                )
            }
            None => {
                // We don't get headers for payment webhook response handling
                let response: FiuuPaymentResponse = res
                    .response
                    .parse_struct("fiuu::FiuuPaymentResponse")
                    .change_context(
                        crate::utils::response_deserialization_fail(
                            res.status_code,
                        "fiuu: response body did not match the expected format; confirm API version and connector documentation."),
                    )?;
                with_response_body!(event_builder, response);

                RouterDataV2::try_from(ResponseRouterData {
                    response,
                    router_data: data.clone(),
                    http_code: res.status_code,
                })
                .change_context(
                    crate::utils::response_handling_fail_for_connector(res.status_code, "fiuu"),
                )
            }
        }
    }
    fn get_error_response_v2(
        &self,
        res: Response,
        event_builder: Option<&mut events::Event>,
        _connector_config: &ConnectorSpecificConfig,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder, _connector_config)
    }
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize>
    connector_types::IncomingWebhook for Fiuu<T>
{
    fn get_webhook_source_verification_signature(
        &self,
        request: &RequestDetails,
        _connector_webhook_secret: &ConnectorWebhookSecrets,
    ) -> Result<Vec<u8>, error_stack::Report<WebhookError>> {
        let header = request
            .headers
            .get("content-type")
            .ok_or_else(|| report!(WebhookError::WebhookBodyDecodingFailed))?;
        let resource: FiuuWebhooksResponse = if header == "application/x-www-form-urlencoded" {
            parse_and_log_keys_in_url_encoded_response::<FiuuWebhooksResponse>(&request.body);
            serde_urlencoded::from_bytes::<FiuuWebhooksResponse>(&request.body)
                .change_context(WebhookError::WebhookSourceVerificationFailed)?
        } else {
            request
                .body
                .parse_struct("fiuu::FiuuWebhooksResponse")
                .change_context(WebhookError::WebhookSourceVerificationFailed)?
        };

        let signature = match resource {
            FiuuWebhooksResponse::FiuuWebhookPaymentResponse(webhooks_payment_response) => {
                webhooks_payment_response.skey
            }
            FiuuWebhooksResponse::FiuuWebhookRefundResponse(webhooks_refunds_response) => {
                webhooks_refunds_response.signature
            }
        };
        hex::decode(signature.expose())
            .change_context(WebhookError::WebhookSourceVerificationFailed)
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &RequestDetails,
        connector_webhook_secrets: &ConnectorWebhookSecrets,
    ) -> Result<Vec<u8>, error_stack::Report<WebhookError>> {
        let header = request
            .headers
            .get("content-type")
            .ok_or_else(|| report!(WebhookError::WebhookBodyDecodingFailed))?;
        let resource: FiuuWebhooksResponse = if header == "application/x-www-form-urlencoded" {
            parse_and_log_keys_in_url_encoded_response::<FiuuWebhooksResponse>(&request.body);
            serde_urlencoded::from_bytes::<FiuuWebhooksResponse>(&request.body)
                .change_context(WebhookError::WebhookSourceVerificationFailed)?
        } else {
            request
                .body
                .parse_struct("fiuu::FiuuWebhooksResponse")
                .change_context(WebhookError::WebhookSourceVerificationFailed)?
        };
        let verification_message = match resource {
            FiuuWebhooksResponse::FiuuWebhookPaymentResponse(webhooks_payment_response) => {
                let key0 = format!(
                    "{}{}{}{}{}{}",
                    webhooks_payment_response.tran_id,
                    webhooks_payment_response.order_id,
                    webhooks_payment_response.status,
                    webhooks_payment_response.domain.clone().peek(),
                    webhooks_payment_response.amount.get_amount_as_string(),
                    webhooks_payment_response.currency
                );
                let md5_key0 = hex::encode(
                    crypto::Md5
                        .generate_digest(key0.as_bytes())
                        .change_context(WebhookError::WebhookSourceVerificationFailed)?,
                );
                let key1 = format!(
                    "{}{}{}{}{}",
                    webhooks_payment_response.paydate,
                    webhooks_payment_response.domain.peek(),
                    md5_key0,
                    webhooks_payment_response
                        .appcode
                        .map_or("".to_string(), |appcode| appcode.expose()),
                    String::from_utf8_lossy(&connector_webhook_secrets.secret)
                );
                key1
            }
            FiuuWebhooksResponse::FiuuWebhookRefundResponse(webhooks_refunds_response) => {
                format!(
                    "{}{}{}{}{}{}{}{}",
                    webhooks_refunds_response.refund_type,
                    webhooks_refunds_response.merchant_id.peek(),
                    webhooks_refunds_response.ref_id,
                    webhooks_refunds_response.refund_id,
                    webhooks_refunds_response.txn_id,
                    webhooks_refunds_response.amount.get_amount_as_string(),
                    webhooks_refunds_response.status,
                    String::from_utf8_lossy(&connector_webhook_secrets.secret)
                )
            }
        };
        Ok(verification_message.as_bytes().to_vec())
    }

    fn verify_webhook_source(
        &self,
        request: RequestDetails,
        connector_webhook_secret: Option<ConnectorWebhookSecrets>,
        _connector_account_details: Option<ConnectorSpecificConfig>,
    ) -> Result<bool, error_stack::Report<WebhookError>> {
        let algorithm = crypto::Md5;

        let connector_webhook_secrets = match connector_webhook_secret {
            Some(secrets) => secrets,
            None => {
                return Err(report!(WebhookError::WebhookVerificationSecretNotFound));
            }
        };

        let signature =
            self.get_webhook_source_verification_signature(&request, &connector_webhook_secrets)?;

        let message =
            self.get_webhook_source_verification_message(&request, &connector_webhook_secrets)?;

        algorithm
            .verify_signature(&connector_webhook_secrets.secret, &signature, &message)
            .change_context(WebhookError::WebhookSourceVerificationFailed)
    }

    fn sample_webhook_body(&self) -> &'static [u8] {
        b"tranID=probe_txn_001&orderid=probe_order_001&status=00&domain=probe_domain&amount=10.00&currency=USD&channel=Credit&skey=probe_skey&appcode=probe_appcode&error_code=&error_desc="
    }

    fn get_event_type(
        &self,
        request: RequestDetails,
    ) -> Result<EventType, error_stack::Report<WebhookError>> {
        if request.body.is_empty() {
            return Ok(EventType::EndpointVerification);
        }
        let header = request
            .headers
            .get("content-type")
            .ok_or_else(|| report!(WebhookError::WebhookBodyDecodingFailed))?;

        let resource: FiuuWebhooksResponse = if header == "application/x-www-form-urlencoded" {
            parse_and_log_keys_in_url_encoded_response::<FiuuWebhooksResponse>(&request.body);
            serde_urlencoded::from_bytes::<FiuuWebhooksResponse>(&request.body)
                .change_context(WebhookError::WebhookBodyDecodingFailed)?
        } else {
            request
                .body
                .parse_struct("fiuu::FiuuWebhooksResponse")
                .change_context(WebhookError::WebhookBodyDecodingFailed)?
        };

        match resource {
            FiuuWebhooksResponse::FiuuWebhookPaymentResponse(webhooks_payment_response) => {
                Ok(EventType::from(webhooks_payment_response.status))
            }
            FiuuWebhooksResponse::FiuuWebhookRefundResponse(webhooks_refunds_response) => {
                Ok(EventType::from(webhooks_refunds_response.status))
            }
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: RequestDetails,
    ) -> Result<Box<dyn hyperswitch_masking::ErasedMaskSerialize>, error_stack::Report<WebhookError>>
    {
        let header = request
            .headers
            .get("content-type")
            .ok_or_else(|| report!(WebhookError::WebhookBodyDecodingFailed))?;

        let payload: FiuuWebhooksResponse = if header == "application/x-www-form-urlencoded" {
            parse_and_log_keys_in_url_encoded_response::<FiuuWebhooksResponse>(&request.body);
            serde_urlencoded::from_bytes::<FiuuWebhooksResponse>(&request.body)
                .change_context(WebhookError::WebhookResourceObjectNotFound)?
        } else {
            request
                .body
                .parse_struct("fiuu::FiuuWebhooksResponse")
                .change_context(WebhookError::WebhookResourceObjectNotFound)?
        };

        match payload.clone() {
            FiuuWebhooksResponse::FiuuWebhookPaymentResponse(webhook_payment_response) => {
                let resource: Box<dyn hyperswitch_masking::ErasedMaskSerialize> = Box::new(
                    FiuuPaymentResponse::FiuuWebhooksPaymentResponse(webhook_payment_response),
                );
                Ok(resource)
            }
            FiuuWebhooksResponse::FiuuWebhookRefundResponse(webhook_refund_response) => {
                let resource: Box<dyn hyperswitch_masking::ErasedMaskSerialize> =
                    Box::new(FiuuRefundSyncResponse::Webhook(webhook_refund_response));
                Ok(resource)
            }
        }
    }

    fn get_webhook_event_reference(
        &self,
        request: RequestDetails,
    ) -> Result<Option<WebhookResourceReference>, error_stack::Report<WebhookError>> {
        let header = request
            .headers
            .get("content-type")
            .ok_or_else(|| report!(WebhookError::WebhookBodyDecodingFailed))?;

        let payload: FiuuWebhooksResponse = if header == "application/x-www-form-urlencoded" {
            parse_and_log_keys_in_url_encoded_response::<FiuuWebhooksResponse>(&request.body);
            serde_urlencoded::from_bytes::<FiuuWebhooksResponse>(&request.body)
                .change_context(WebhookError::WebhookResourceObjectNotFound)?
        } else {
            request
                .body
                .parse_struct("fiuu::FiuuWebhooksResponse")
                .change_context(WebhookError::WebhookResourceObjectNotFound)?
        };

        let reference = match payload {
            FiuuWebhooksResponse::FiuuWebhookPaymentResponse(p) => {
                WebhookResourceReference::Payment(PaymentWebhookReference {
                    connector_transaction_id: Some(p.tran_id),
                    // order_id is the merchant-assigned order reference echoed back by Fiuu.
                    merchant_transaction_id: Some(p.order_id),
                })
            }
            FiuuWebhooksResponse::FiuuWebhookRefundResponse(r) => {
                WebhookResourceReference::Refund(RefundWebhookReference {
                    connector_refund_id: Some(r.refund_id),
                    merchant_refund_id: None,
                    // txn_id is the connector ID of the original payment.
                    connector_transaction_id: Some(r.txn_id),
                    merchant_transaction_id: None,
                })
            }
        };

        Ok(Some(reference))
    }

    fn process_payment_webhook(
        &self,
        request: RequestDetails,
        _connector_webhook_secret: Option<ConnectorWebhookSecrets>,
        _connector_account_details: Option<ConnectorSpecificConfig>,
        event_context: Option<EventContext>,
    ) -> Result<WebhookDetailsResponse, error_stack::Report<WebhookError>> {
        let header = request
            .headers
            .get("content-type")
            .ok_or_else(|| report!(WebhookError::WebhookBodyDecodingFailed))?;

        let payload: FiuuWebhooksResponse = if header == "application/x-www-form-urlencoded" {
            parse_and_log_keys_in_url_encoded_response::<FiuuWebhooksResponse>(&request.body);
            serde_urlencoded::from_bytes::<FiuuWebhooksResponse>(&request.body)
                .change_context(WebhookError::WebhookResourceObjectNotFound)?
        } else {
            request
                .body
                .parse_struct("fiuu::FiuuWebhooksResponse")
                .change_context(WebhookError::WebhookResourceObjectNotFound)?
        };

        let webhook_payment = match payload {
            FiuuWebhooksResponse::FiuuWebhookPaymentResponse(payment_response) => payment_response,
            FiuuWebhooksResponse::FiuuWebhookRefundResponse(_) => {
                return Err(
                    report!(WebhookError::WebhookResourceObjectNotFound).attach_printable(
                        "Received refund payload in process_payment_webhook for Fiuu",
                    ),
                );
            }
        };

        let capture_method = event_context.and_then(|ctx| ctx.capture_method);

        // Fiuu webhook status "00" (Success) does not distinguish AUTHORIZED from CAPTURED.
        // capture_method from the original authorize request is required to resolve this.
        if webhook_payment.status == fiuu::FiuuPaymentWebhookStatus::Success
            && capture_method.is_none()
        {
            return Err(
                error_stack::report!(WebhookError::WebhookMissingRequiredContext {
                    field: "capture_method",
                    origin: "payment authorize",
                })
                .attach_printable(
                    "Fiuu webhook status '00' (Success) is ambiguous without capture_method: \
                 AUTOMATIC capture means the payment was Charged, MANUAL means Authorized. \
                 Pass EventContext.payment.capture_method from your original authorize request.",
                ),
            );
        }

        let status = common_enums::AttemptStatus::try_from(fiuu::FiuuWebhookStatus {
            capture_method,
            status: webhook_payment.status.clone(),
        })
        .change_context(WebhookError::WebhookBodyDecodingFailed)?;

        let error_code = webhook_payment.error_code.clone();
        let error_message = webhook_payment.error_desc.clone();
        Ok(WebhookDetailsResponse {
            resource_id: Some(
                domain_types::connector_types::ResponseId::ConnectorTransactionId(
                    webhook_payment.tran_id.clone(),
                ),
            ),
            status,
            connector_response_reference_id: Some(webhook_payment.order_id.clone()),
            connector_request_reference_id: Some(webhook_payment.order_id),
            error_code: error_code.clone(),
            error_message: error_message.clone(),
            raw_connector_response: Some(String::from_utf8_lossy(&request.body).to_string()),
            status_code: 200,
            response_headers: None,
            mandate_reference: webhook_payment
                .extra_parameters
                .as_ref()
                .and_then(|extra_params| {
                    serde_json::from_str::<fiuu::ExtraParameters>(&extra_params.clone().expose())
                        .ok()
                        .and_then(|extra| extra.token)
                        .map(|token| {
                            Box::new(domain_types::connector_types::MandateReference {
                                connector_mandate_id: Some(token.expose()),
                                payment_method_id: None,
                                connector_mandate_request_reference_id: None,
                                mandate_metadata: None,
                            })
                        })
                }),
            minor_amount_captured: None,
            amount_captured: None,
            error_reason: error_message,
            network_txn_id: None,
            payment_method_update: None,
            sender_payment_instrument_id: None,
        })
    }

    fn process_refund_webhook(
        &self,
        request: RequestDetails,
        _connector_webhook_secret: Option<ConnectorWebhookSecrets>,
        _connector_account_details: Option<ConnectorSpecificConfig>,
    ) -> Result<RefundWebhookDetailsResponse, error_stack::Report<WebhookError>> {
        let header = request
            .headers
            .get("content-type")
            .ok_or_else(|| report!(WebhookError::WebhookBodyDecodingFailed))?;

        let payload: FiuuWebhooksResponse = if header == "application/x-www-form-urlencoded" {
            parse_and_log_keys_in_url_encoded_response::<FiuuWebhooksResponse>(&request.body);
            serde_urlencoded::from_bytes::<FiuuWebhooksResponse>(&request.body)
                .change_context(WebhookError::WebhookResourceObjectNotFound)?
        } else {
            request
                .body
                .parse_struct("fiuu::FiuuWebhooksResponse")
                .change_context(WebhookError::WebhookResourceObjectNotFound)?
        };

        let notif = match payload.clone() {
            FiuuWebhooksResponse::FiuuWebhookPaymentResponse(_) => {
                Err(report!(WebhookError::WebhookResourceObjectNotFound))
            }
            FiuuWebhooksResponse::FiuuWebhookRefundResponse(webhook_refund_response) => {
                Ok(FiuuRefundSyncResponse::Webhook(webhook_refund_response))
            }
        }?;

        let response = RefundWebhookDetailsResponse::try_from(notif)
            .change_context(WebhookError::WebhookBodyDecodingFailed);

        response.map(|mut response| {
            response.raw_connector_response =
                Some(String::from_utf8_lossy(&request.body).to_string());
            response
        })
    }
}

impl<T: PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize> ConnectorSpecifications
    for Fiuu<T>
{
}

fn parse_response<T>(
    data: &[u8],
    http_status: u16,
) -> Result<T, error_stack::Report<ConnectorError>>
where
    T: for<'de> Deserialize<'de>,
{
    let response_str = String::from_utf8(data.to_vec()).map_err(|e| {
        error!("Error in Deserializing Response Data: {:?}", e);
        error_stack::Report::from(
            crate::utils::response_deserialization_fail(http_status, "fiuu: response body did not match the expected format; confirm API version and connector documentation."),
        )
    })?;

    let mut json = serde_json::Map::new();
    let mut miscellaneous: HashMap<String, Secret<String>> = HashMap::new();

    for line in response_str.lines() {
        if let Some((key, value)) = line.split_once('=') {
            if key.trim().is_empty() {
                error!("Null or empty key encountered in response.");
                continue;
            }

            if let Some(old_value) = json.insert(key.to_string(), Value::String(value.to_string()))
            {
                warn!("Repeated key encountered: {}", key);
                miscellaneous.insert(key.to_string(), Secret::new(old_value.to_string()));
            }
        }
    }
    if !miscellaneous.is_empty() {
        let misc_value = serde_json::to_value(miscellaneous).map_err(|e| {
            error!("Error serializing miscellaneous data: {:?}", e);
            error_stack::Report::from(
                crate::utils::response_deserialization_fail(http_status, "fiuu: response body did not match the expected format; confirm API version and connector documentation."),
            )
        })?;
        json.insert("miscellaneous".to_string(), misc_value);
    }

    let response: T = serde_json::from_value(Value::Object(json)).map_err(|e| {
        error!("Error in Deserializing Response Data: {:?}", e);
        error_stack::Report::from(
            crate::utils::response_deserialization_fail(http_status, "fiuu: response body did not match the expected format; confirm API version and connector documentation."),
        )
    })?;

    Ok(response)
}

pub fn parse_and_log_keys_in_url_encoded_response<T>(data: &[u8]) {
    match std::str::from_utf8(data) {
        Ok(query_str) => {
            let loggable_keys = [
                "status",
                "orderid",
                "tranID",
                "nbcb",
                "amount",
                "currency",
                "paydate",
                "channel",
                "error_desc",
                "error_code",
                "extraP",
            ];
            let keys: Vec<(Cow<'_, str>, String)> =
                url::form_urlencoded::parse(query_str.as_bytes())
                    .map(|(key, value)| {
                        if loggable_keys.contains(&key.to_string().as_str()) {
                            (key, value.to_string())
                        } else {
                            (key, "SECRET".to_string())
                        }
                    })
                    .collect();
            info!("Keys in {} response\n{:?}", type_name::<T>(), keys);
        }
        Err(err) => {
            error!("Failed to convert bytes to string: {:?}", err);
        }
    }
}

macros::macro_connector_flow_status_impls!(
    connector: Fiuu,
    generic_type: T,
    [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
    not_implemented: [
        IncrementalAuthorization,
        VoidPC,
        CreateOrder,
        ServerSessionAuthenticationToken,
        ServerAuthenticationToken,
        CreateConnectorCustomer,
        GetConnectorCustomer,
        MandateRevoke,
        PaymentMethodToken,
        PreAuthenticate,
        Authenticate,
        PostAuthenticate,
        ClientAuthenticationToken,
    ],
    not_supported: [
        VoidPostRefund,
        Accept,
        SubmitEvidence,
        DefendDispute,
    ],
);
