use actix_web::{web, HttpRequest, HttpResponse};
use router_env::{instrument, tracing, Flow};

use super::app::AppState;
use crate::{
    core::{admin::*, api_locking, errors, merchant_connector_webhook_management::*},
    services::{api, authentication as auth, authorization::permissions::Permission},
    types::api::admin,
};

#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::OrganizationCreate))]
pub async fn organization_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<admin::OrganizationCreateRequest>,
) -> HttpResponse {
    let flow = Flow::OrganizationCreate;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, _, req, _| create_organization(state, req),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
#[cfg(all(feature = "olap", feature = "v2"))]
#[instrument(skip_all, fields(flow = ?Flow::OrganizationCreate))]
pub async fn organization_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<admin::OrganizationCreateRequest>,
) -> HttpResponse {
    let flow = Flow::OrganizationCreate;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, _, req, _| create_organization(state, req),
        &auth::V2AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::OrganizationUpdate))]
pub async fn organization_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    org_id: web::Path<common_utils::id_type::OrganizationId>,
    json_payload: web::Json<admin::OrganizationUpdateRequest>,
) -> HttpResponse {
    let flow = Flow::OrganizationUpdate;
    let organization_id = org_id.into_inner();
    let org_id = admin::OrganizationId {
        organization_id: organization_id.clone(),
    };
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, _, req, _| update_organization(state, org_id.clone(), req),
        auth::auth_type(
            &auth::PlatformOrgAdminAuth {
                is_admin_auth_allowed: true,
                organization_id: Some(organization_id.clone()),
            },
            &auth::JWTAuthOrganizationFromRoute {
                organization_id,
                required_permission: Permission::OrganizationAccountWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
#[cfg(all(feature = "olap", feature = "v2"))]
#[instrument(skip_all, fields(flow = ?Flow::OrganizationUpdate))]
pub async fn organization_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    org_id: web::Path<common_utils::id_type::OrganizationId>,
    json_payload: web::Json<admin::OrganizationUpdateRequest>,
) -> HttpResponse {
    let flow = Flow::OrganizationUpdate;
    let organization_id = org_id.into_inner();
    let org_id = admin::OrganizationId {
        organization_id: organization_id.clone(),
    };
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, _, req, _| update_organization(state, org_id.clone(), req),
        auth::auth_type(
            &auth::V2AdminApiAuth,
            &auth::JWTAuthOrganizationFromRoute {
                organization_id,
                required_permission: Permission::OrganizationAccountWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::OrganizationRetrieve))]
pub async fn organization_retrieve(
    state: web::Data<AppState>,
    req: HttpRequest,
    org_id: web::Path<common_utils::id_type::OrganizationId>,
) -> HttpResponse {
    let flow = Flow::OrganizationRetrieve;
    let organization_id = org_id.into_inner();
    let payload = admin::OrganizationId {
        organization_id: organization_id.clone(),
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| get_organization(state, req),
        auth::auth_type(
            &auth::PlatformOrgAdminAuth {
                is_admin_auth_allowed: true,
                organization_id: Some(organization_id.clone()),
            },
            &auth::JWTAuthOrganizationFromRoute {
                organization_id,
                required_permission: Permission::OrganizationAccountRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "olap", feature = "v2"))]
#[instrument(skip_all, fields(flow = ?Flow::OrganizationRetrieve))]
pub async fn organization_retrieve(
    state: web::Data<AppState>,
    req: HttpRequest,
    org_id: web::Path<common_utils::id_type::OrganizationId>,
) -> HttpResponse {
    let flow = Flow::OrganizationRetrieve;
    let organization_id = org_id.into_inner();
    let payload = admin::OrganizationId {
        organization_id: organization_id.clone(),
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| get_organization(state, req),
        auth::auth_type(
            &auth::V2AdminApiAuth,
            &auth::JWTAuthOrganizationFromRoute {
                organization_id,
                required_permission: Permission::OrganizationAccountRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::ConvertOrganizationToPlatform))]
pub async fn convert_organization_to_platform(
    state: web::Data<AppState>,
    req: HttpRequest,
    org_id: web::Path<common_utils::id_type::OrganizationId>,
    json_payload: web::Json<admin::ConvertOrganizationToPlatformRequest>,
) -> HttpResponse {
    let flow = Flow::ConvertOrganizationToPlatform;
    let organization_id = org_id.into_inner();
    let org_id = admin::OrganizationId {
        organization_id: organization_id.clone(),
    };
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, _, req, _| {
            convert_organization_to_platform_organization(state, org_id.clone(), req)
        },
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
pub async fn merchant_account_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<admin::MerchantAccountCreate>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountCreate;
    let payload = json_payload.into_inner();
    if let Err(api_error) = payload
        .validate()
        .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
    {
        return api::log_and_return_error_response(api_error.into());
    }
    if let Err(api_error) = payload
        .webhook_details
        .as_ref()
        .map(|details| {
            details
                .validate()
                .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
        })
        .transpose()
    {
        return api::log_and_return_error_response(api_error.into());
    }

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth, req, _| create_merchant_account(state, req, auth),
        &auth::PlatformOrgAdminAuth {
            is_admin_auth_allowed: true,
            organization_id: None,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "olap", feature = "v2"))]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
pub async fn merchant_account_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<api_models::admin::MerchantAccountCreateWithoutOrgId>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountCreate;
    let headers = req.headers();

    let org_id = match auth::HeaderMapStruct::new(headers).get_organization_id_from_header() {
        Ok(org_id) => org_id,
        Err(e) => return api::log_and_return_error_response(e),
    };

    // Converting from MerchantAccountCreateWithoutOrgId to MerchantAccountCreate so we can use the existing
    // `create_merchant_account` function for v2 as well
    let json_payload = json_payload.into_inner();
    if let Err(api_error) = json_payload
        .validate()
        .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
    {
        return api::log_and_return_error_response(api_error.into());
    }
    let new_request_payload_with_org_id = api_models::admin::MerchantAccountCreate {
        merchant_name: json_payload.merchant_name,
        merchant_details: json_payload.merchant_details,
        metadata: json_payload.metadata,
        organization_id: org_id,
        product_type: json_payload.product_type,
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        new_request_payload_with_org_id,
        |state, _, req, _| create_merchant_account(state, req, None),
        &auth::V2AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountRetrieve))]
pub async fn retrieve_merchant_account(
    state: web::Data<AppState>,
    req: HttpRequest,
    mid: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountRetrieve;
    let merchant_id = mid.into_inner();
    let payload = admin::MerchantId {
        merchant_id: merchant_id.clone(),
    };
    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| get_merchant_account(state, req, None),
        auth::auth_type(
            &auth::PlatformOrgAdminAuthWithMerchantIdFromRoute {
                merchant_id_from_route: merchant_id.clone(),
                is_admin_auth_allowed: true,
            },
            &auth::JWTAuthMerchantFromRoute {
                merchant_id,
                // This should ideally be MerchantAccountRead, but since FE is calling this API for
                // profile level users currently keeping this as ProfileAccountRead. FE is removing
                // this API call for profile level users.
                // TODO: Convert this to MerchantAccountRead once FE changes are done.
                required_permission: Permission::ProfileAccountRead,
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    )
    .await
}

/// Merchant Account - Retrieve
///
/// Retrieve a merchant account details.
#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountRetrieve))]
pub async fn retrieve_merchant_account(
    state: web::Data<AppState>,
    req: HttpRequest,
    mid: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountRetrieve;
    let merchant_id = mid.into_inner();
    let payload = admin::MerchantId {
        merchant_id: merchant_id.clone(),
    };
    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| get_merchant_account(state, req, None),
        auth::auth_type(
            &auth::V2AdminApiAuth,
            &auth::JWTAuthMerchantFromRoute {
                merchant_id,
                // This should ideally be MerchantAccountRead, but since FE is calling this API for
                // profile level users currently keeping this as ProfileAccountRead. FE is removing
                // this API call for profile level users.
                // TODO: Convert this to MerchantAccountRead once FE changes are done.
                required_permission: Permission::ProfileAccountRead,
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    )
    .await
}

#[cfg(all(feature = "olap", feature = "v2"))]
#[instrument(skip_all, fields(flow = ?Flow::MerchantAccountList))]
pub async fn merchant_account_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    organization_id: web::Path<common_utils::id_type::OrganizationId>,
) -> HttpResponse {
    let flow = Flow::MerchantAccountList;

    let organization_id = admin::OrganizationId {
        organization_id: organization_id.into_inner(),
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        organization_id,
        |state, _, request, _| list_merchant_account(state, request),
        auth::auth_type(
            &auth::V2AdminApiAuth,
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantAccountRead,
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::MerchantAccountList))]
pub async fn merchant_account_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    query_params: web::Query<api_models::admin::MerchantAccountListRequest>,
) -> HttpResponse {
    let flow = Flow::MerchantAccountList;

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        query_params.into_inner(),
        |state, auth, request, _| list_merchant_account(state, request, auth),
        auth::auth_type(
            &auth::PlatformOrgAdminAuth {
                is_admin_auth_allowed: true,
                organization_id: None,
            },
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantAccountRead,
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

/// Merchant Account - Update
///
/// To update an existing merchant account. Helpful in updating merchant details such as email, contact details, or other configuration details like webhook, routing algorithm etc
#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountUpdate))]
pub async fn update_merchant_account(
    state: web::Data<AppState>,
    req: HttpRequest,
    mid: web::Path<common_utils::id_type::MerchantId>,
    json_payload: web::Json<admin::MerchantAccountUpdate>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountUpdate;
    let merchant_id = mid.into_inner();
    let payload = json_payload.into_inner();
    if let Err(api_error) = payload
        .validate()
        .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
    {
        return api::log_and_return_error_response(api_error.into());
    }
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| merchant_account_update(state, &merchant_id, None, req),
        auth::auth_type(
            &auth::V2AdminApiAuth,
            &auth::JWTAuthMerchantFromRoute {
                merchant_id: merchant_id.clone(),
                required_permission: Permission::MerchantAccountWrite,
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountUpdate))]
pub async fn update_merchant_account(
    state: web::Data<AppState>,
    req: HttpRequest,
    mid: web::Path<common_utils::id_type::MerchantId>,
    json_payload: web::Json<admin::MerchantAccountUpdate>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountUpdate;
    let merchant_id = mid.into_inner();
    let payload = json_payload.into_inner();
    if let Err(api_error) = payload
        .validate()
        .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
    {
        return api::log_and_return_error_response(api_error.into());
    }
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| merchant_account_update(state, &merchant_id, None, req),
        auth::auth_type(
            &auth::PlatformOrgAdminAuthWithMerchantIdFromRoute {
                merchant_id_from_route: merchant_id.clone(),
                is_admin_auth_allowed: true,
            },
            &auth::JWTAuthMerchantFromRoute {
                merchant_id: merchant_id.clone(),
                required_permission: Permission::MerchantAccountWrite,
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

/// Merchant Account - Delete
///
/// To delete a merchant account
#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountDelete))]
pub async fn delete_merchant_account(
    state: web::Data<AppState>,
    req: HttpRequest,
    mid: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountDelete;
    let mid = mid.into_inner();

    let payload = web::Json(admin::MerchantId { merchant_id: mid }).into_inner();
    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| merchant_account_delete(state, req.merchant_id),
        &auth::V2AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    )
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountDelete))]
pub async fn delete_merchant_account(
    state: web::Data<AppState>,
    req: HttpRequest,
    mid: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountDelete;
    let mid = mid.into_inner();

    let payload = web::Json(admin::MerchantId { merchant_id: mid }).into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| merchant_account_delete(state, req.merchant_id),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

/// Merchant Connector - Create
///
/// Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc."
#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsCreate))]
pub async fn connector_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
    json_payload: web::Json<admin::MerchantConnectorCreate>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsCreate;
    let payload = json_payload.into_inner();
    let merchant_id = path.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth_data, req, _| async move {
            Box::pin(create_connector(
                state,
                req,
                auth_data.platform.get_processor().clone(),
                auth_data.profile.map(|profile| profile.get_id().clone()),
            ))
            .await
        },
        auth::auth_type(
            &auth::ApiKeyAuthWithMerchantIdFromRouteAllowPlatform(merchant_id.clone()),
            &auth::JWTAndEmbeddedAuth {
                merchant_id_from_route: Some(merchant_id.clone()),
                permission: Some(Permission::ProfileConnectorWrite),
                allow_connected: true,
                // Platform merchants create the external (vault) connector on themselves.
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
/// Merchant Connector - Create
///
/// Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc."
#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsCreate))]
pub async fn connector_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<admin::MerchantConnectorCreate>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsCreate;
    let payload = json_payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth_data: auth::AuthenticationData, req, _| async move {
            Box::pin(create_connector(
                state,
                req,
                auth_data.platform.get_processor().clone(),
                None,
            ))
            .await
        },
        auth::auth_type(
            &auth::AdminApiAuthWithMerchantIdFromHeader,
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantConnectorWrite,
                allow_connected: true,
                // Platform merchants create the external (vault) connector on themselves.
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
/// Merchant Connector - Retrieve
///
/// Retrieve Merchant Connector Details
#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsRetrieve))]
pub async fn connector_retrieve(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsRetrieve;
    let (merchant_id, merchant_connector_id) = path.into_inner();
    let payload = web::Json(admin::MerchantConnectorId {
        merchant_id: merchant_id.clone(),
        merchant_connector_id,
    })
    .into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth, req, _| {
            retrieve_connector(
                state,
                auth.platform.get_processor().clone(),
                auth.profile.map(|profile| profile.get_id().clone()),
                req.merchant_connector_id,
            )
        },
        auth::auth_type(
            &auth::ApiKeyAuthWithMerchantIdFromRouteAllowPlatform(merchant_id.clone()),
            &auth::JWTAndEmbeddedAuth {
                merchant_id_from_route: Some(merchant_id.clone()),
                // This should ideally be ProfileConnectorRead, but since this API responds with
                // sensitive data, keeping this as ProfileConnectorWrite
                // TODO: Convert this to ProfileConnectorRead once data is masked.
                permission: Some(Permission::ProfileConnectorWrite),
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
/// Merchant Connector - Retrieve
///
/// Retrieve Merchant Connector Details
#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsRetrieve))]
pub async fn connector_retrieve(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantConnectorAccountId>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsRetrieve;
    let id = path.into_inner();
    let payload = web::Json(admin::MerchantConnectorId { id: id.clone() }).into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            retrieve_connector(state, auth.platform.get_processor().clone(), req.id.clone())
        },
        auth::auth_type(
            &auth::AdminApiAuthWithMerchantIdFromHeader,
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantConnectorRead,
                allow_connected: true,
                allow_platform: false,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "olap", feature = "v2"))]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsList))]
pub async fn connector_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::ProfileId>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsList;
    let profile_id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        profile_id.to_owned(),
        |state, auth: auth::AuthenticationData, _, _| {
            list_connectors_for_a_profile(
                state,
                auth.platform.get_processor().clone(),
                profile_id.clone(),
            )
        },
        auth::auth_type(
            &auth::AdminApiAuthWithMerchantIdFromHeader,
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantConnectorRead,
                allow_connected: true,
                allow_platform: false,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

/// Merchant Connector - List
///
/// List Merchant Connector Details for the merchant
#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsList))]
pub async fn connector_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsList;
    let merchant_id = path.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        merchant_id.to_owned(),
        |state, auth, _, _| {
            list_payment_connectors(state, auth.platform.get_processor().clone(), None)
        },
        auth::auth_type(
            &auth::ApiKeyAuthWithMerchantIdFromRouteAllowPlatform(merchant_id.clone()),
            &auth::JWTAuthMerchantFromRoute {
                merchant_id,
                required_permission: Permission::MerchantConnectorRead,
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    )
    .await
}

#[cfg(all(feature = "v1", feature = "olap"))]
/// Merchant Connector - List
///
/// List Merchant Connector Details for the merchant
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsList))]
pub async fn connector_list_profile(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsList;
    let merchant_id = path.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        merchant_id.to_owned(),
        |state, auth, _, _| {
            list_payment_connectors(
                state,
                auth.platform.get_processor().clone(),
                auth.profile.map(|profile| vec![profile.get_id().clone()]),
            )
        },
        auth::auth_type(
            &auth::ApiKeyAuthWithMerchantIdFromRouteAllowPlatform(merchant_id.clone()),
            &auth::JWTAndEmbeddedAuth {
                merchant_id_from_route: Some(merchant_id),
                permission: Some(Permission::ProfileConnectorRead),
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    )
    .await
}

/// Merchant Connector - Update
///
/// To update an existing Merchant Connector. Helpful in enabling / disabling different payment methods and other settings for the connector etc.
#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsUpdate))]
pub async fn connector_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
    json_payload: web::Json<api_models::admin::MerchantConnectorUpdate>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsUpdate;
    let (merchant_id, merchant_connector_id) = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, auth, req, _| {
            update_connector(
                state,
                auth.platform.get_processor().get_account().get_id().clone(),
                auth.profile.map(|profile| profile.get_id().clone()),
                &merchant_connector_id,
                req,
            )
        },
        auth::auth_type(
            &auth::ApiKeyAuthWithMerchantIdFromRouteAllowPlatform(merchant_id.clone()),
            &auth::JWTAndEmbeddedAuth {
                merchant_id_from_route: Some(merchant_id.clone()),
                permission: Some(Permission::ProfileConnectorWrite),
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

/// Merchant Connector - Update
///
/// To update an existing Merchant Connector. Helpful in enabling / disabling different payment methods and other settings for the connector etc.
#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsUpdate))]
pub async fn connector_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantConnectorAccountId>,
    json_payload: web::Json<api_models::admin::MerchantConnectorUpdate>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsUpdate;
    let id = path.into_inner();
    let payload = json_payload.into_inner();
    let merchant_id = payload.merchant_id.clone();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| update_connector(state, merchant_id.clone(), None, &id, req),
        auth::auth_type(
            &auth::V2AdminApiAuth,
            &auth::JWTAuthMerchantFromRoute {
                merchant_id: merchant_id.clone(),
                required_permission: Permission::MerchantConnectorWrite,
                allow_connected: true,
                allow_platform: false,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
/// Merchant Connector - Delete
///
/// Delete or Detach a Merchant Connector from Merchant Account
#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsDelete))]
pub async fn connector_delete(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsDelete;
    let (merchant_id, merchant_connector_id) = path.into_inner();

    let payload = web::Json(admin::MerchantConnectorId {
        merchant_id: merchant_id.clone(),
        merchant_connector_id,
    })
    .into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, req, _| delete_connector(state, req.merchant_id, req.merchant_connector_id),
        auth::auth_type(
            &auth::AdminApiAuth,
            &auth::JWTAuthMerchantFromRoute {
                merchant_id,
                required_permission: Permission::MerchantConnectorWrite,
                allow_connected: true,
                allow_platform: false,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
/// Merchant Connector - Delete
///
/// Delete or Detach a Merchant Connector from Merchant Account
#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsDelete))]
pub async fn connector_delete(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantConnectorAccountId>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorsDelete;
    let id = path.into_inner();

    let payload = web::Json(admin::MerchantConnectorId { id: id.clone() }).into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            delete_connector(state, auth.platform, req.id)
        },
        auth::auth_type(
            &auth::AdminApiAuthWithMerchantIdFromHeader,
            &auth::JWTAuthMerchantFromHeader {
                required_permission: Permission::MerchantConnectorWrite,
                allow_connected: true,
                allow_platform: false,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
/// Merchant Account - Toggle KV
///
/// Toggle KV mode for the Merchant Account
#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn merchant_account_toggle_kv(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
    json_payload: web::Json<admin::ToggleKVRequest>,
) -> HttpResponse {
    let flow = Flow::ConfigKeyUpdate;
    let mut payload = json_payload.into_inner();
    payload.merchant_id = path.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, payload, _| kv_for_merchant(state, payload.merchant_id, payload.kv_enabled),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    )
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn merchant_account_toggle_kv(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
    json_payload: web::Json<admin::ToggleKVRequest>,
) -> HttpResponse {
    let flow = Flow::ConfigKeyUpdate;
    let mut payload = json_payload.into_inner();
    payload.merchant_id = path.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, payload, _| kv_for_merchant(state, payload.merchant_id, payload.kv_enabled),
        &auth::V2AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    )
    .await
}
/// Merchant Account - Transfer Keys
///
/// Transfer Merchant Encryption key to keymanager
#[instrument(skip_all)]
pub async fn merchant_account_toggle_all_kv(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<admin::ToggleAllKVRequest>,
) -> HttpResponse {
    let flow = Flow::MerchantTransferKey;
    let payload = json_payload.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _, payload, _| toggle_kv_for_all_merchants(state, payload.kv_enabled),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    )
    .await
}

/// Merchant Account - KV Status
///
/// Toggle KV mode for the Merchant Account
#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn merchant_account_kv_status(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::ConfigKeyFetch;
    let merchant_id = path.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        merchant_id,
        |state, _, req, _| check_merchant_account_kv_status(state, req),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    )
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn merchant_account_kv_status(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse {
    let flow = Flow::ConfigKeyFetch;
    let merchant_id = path.into_inner();

    api::server_wrap(
        flow,
        state,
        &req,
        merchant_id,
        |state, _, req, _| check_merchant_account_kv_status(state, req),
        &auth::V2AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    )
    .await
}
/// Merchant Account - KV Status
///
/// Toggle KV mode for the Merchant Account
#[instrument(skip_all)]
pub async fn merchant_account_transfer_keys(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Json<api_models::admin::MerchantKeyTransferRequest>,
) -> HttpResponse {
    let flow = Flow::ConfigKeyFetch;
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload.into_inner(),
        |state, _, req, _| transfer_key_store_to_key_manager(state, req),
        &auth::AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

/// Configure Connector Webhook - Register
///
/// To setup webhook configuration for an existing Merchant at the connector.
#[cfg(feature = "v1")]
#[
    instrument(skip_all, fields(flow = ?Flow::MerchantConnectorWebhookRegister))]
pub async fn connector_webhook_register(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
    json_payload: web::Json<
        api_models::merchant_connector_webhook_management::ConnectorWebhookRegisterRequest,
    >,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorWebhookRegister;
    let (merchant_id, merchant_connector_id) = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, auth, req, _| {
            register_connector_webhook(
                state,
                &merchant_id,
                auth.profile.map(|profile| profile.get_id().clone()),
                &merchant_connector_id,
                req,
            )
        },
        auth::auth_type(
            &auth::ApiKeyAuthWithMerchantIdFromRoute(merchant_id.clone()),
            &auth::JWTAuthMerchantFromRoute {
                merchant_id: merchant_id.clone(),
                required_permission: Permission::ProfileConnectorWrite,
                allow_connected: true,
                allow_platform: false,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

/// Configure Connector Webhook - Register
///
/// List webhooks configured with hyperswitch at the connector
#[cfg(feature = "v1")]
#[
    instrument(skip_all, fields(flow = ?Flow::MerchantConnectorWebhookList))]
pub async fn retrieve_connector_webhook(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
) -> HttpResponse {
    let flow = Flow::MerchantConnectorWebhookList;
    let (merchant_id, merchant_connector_id) = path.into_inner();
    let payload = web::Json(admin::MerchantConnectorId {
        merchant_id: merchant_id.clone(),
        merchant_connector_id,
    })
    .into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth, req, _| {
            fetch_connector_webhook(
                state,
                req.merchant_id.clone(),
                auth.profile.map(|profile| profile.get_id().clone()),
                req.merchant_connector_id,
            )
        },
        auth::auth_type(
            &auth::ApiKeyAuthWithMerchantIdFromRoute(merchant_id.clone()),
            &auth::JWTAuthMerchantFromRoute {
                merchant_id,
                required_permission: Permission::ProfileAccountRead,
                allow_connected: true,
                allow_platform: true,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
