use std::{collections::HashMap, ops::Not};

use common_enums::{EntityType, ParentGroup, PermissionGroup, PermissionScope, Resource};
use strum::IntoEnumIterator;

use super::permissions;
use crate::db::domain::role::RoleProductCategory;

pub trait PermissionGroupExt {
    fn scope(&self) -> PermissionScope;
    fn parent(&self) -> ParentGroup;
    fn resources(&self) -> Vec<Resource>;
    fn accessible_groups(&self) -> Vec<PermissionGroup>;
    fn get_role_product_category(&self) -> RoleProductCategory;
}

impl PermissionGroupExt for PermissionGroup {
    fn scope(&self) -> PermissionScope {
        match self {
            Self::OperationsView
            | Self::ConnectorsView
            | Self::WorkflowsView
            | Self::AnalyticsView
            | Self::UsersView
            | Self::AccountView
            | Self::WebhooksView
            | Self::ApiKeysView
            | Self::ThemeView
            | Self::ConfigurationsView
            | Self::ReconSourcesView
            | Self::ReconTransactionsView
            | Self::ReconExceptionsView
            | Self::ReconRulesView => PermissionScope::Read,

            Self::OperationsManage
            | Self::ConnectorsManage
            | Self::WorkflowsManage
            | Self::UsersManage
            | Self::AccountManage
            | Self::WebhooksManage
            | Self::ApiKeysManage
            | Self::CloneConnectorManage
            | Self::ThemeManage
            | Self::ConfigurationsManage
            | Self::ReconSourcesManage
            | Self::ReconExceptionsManage
            | Self::ReconTransactionsManage
            | Self::ReconRulesManage => PermissionScope::Write,
        }
    }

    fn parent(&self) -> ParentGroup {
        match self {
            Self::OperationsView | Self::OperationsManage => ParentGroup::Operations,
            Self::ConnectorsView | Self::ConnectorsManage => ParentGroup::Connectors,
            Self::WorkflowsView | Self::WorkflowsManage => ParentGroup::Workflows,
            Self::AnalyticsView => ParentGroup::Analytics,
            Self::UsersView | Self::UsersManage => ParentGroup::Users,
            Self::AccountView | Self::AccountManage => ParentGroup::Account,
            Self::WebhooksView | Self::WebhooksManage => ParentGroup::Webhook,
            Self::ApiKeysView | Self::ApiKeysManage => ParentGroup::ApiKeys,

            Self::ThemeView | Self::ThemeManage => ParentGroup::Theme,
            Self::ConfigurationsView | Self::ConfigurationsManage => ParentGroup::Configurations,
            Self::CloneConnectorManage => ParentGroup::CloneConnector,
            Self::ReconSourcesView | Self::ReconSourcesManage => ParentGroup::ReconSources,
            Self::ReconExceptionsView | Self::ReconExceptionsManage => ParentGroup::ReconExceptions,
            Self::ReconTransactionsView | Self::ReconTransactionsManage => {
                ParentGroup::ReconTransactions
            }
            Self::ReconRulesView | Self::ReconRulesManage => ParentGroup::ReconRules,
        }
    }

    fn resources(&self) -> Vec<Resource> {
        self.parent().resources()
    }

    fn accessible_groups(&self) -> Vec<Self> {
        match self {
            Self::OperationsView => vec![Self::OperationsView, Self::ConnectorsView],
            Self::OperationsManage => vec![
                Self::OperationsView,
                Self::OperationsManage,
                Self::ConnectorsView,
            ],

            Self::ConnectorsView => vec![Self::ConnectorsView],
            Self::ConnectorsManage => vec![Self::ConnectorsView, Self::ConnectorsManage],

            Self::WorkflowsView => vec![Self::WorkflowsView, Self::ConnectorsView],
            Self::WorkflowsManage => vec![
                Self::WorkflowsView,
                Self::WorkflowsManage,
                Self::ConnectorsView,
            ],

            Self::AnalyticsView => vec![Self::AnalyticsView, Self::OperationsView],

            Self::UsersView => vec![Self::UsersView],
            Self::UsersManage => {
                vec![Self::UsersView, Self::UsersManage]
            }

            Self::AccountView => vec![Self::AccountView],
            Self::AccountManage => vec![Self::AccountView, Self::AccountManage],

            Self::WebhooksView => vec![Self::WebhooksView, Self::AccountView],
            Self::WebhooksManage => {
                vec![Self::WebhooksView, Self::WebhooksManage, Self::AccountView]
            }

            Self::ApiKeysView => vec![Self::ApiKeysView, Self::AccountView],
            Self::ApiKeysManage => vec![Self::ApiKeysView, Self::ApiKeysManage, Self::AccountView],

            Self::CloneConnectorManage => vec![Self::CloneConnectorManage],
            Self::ThemeView => vec![Self::ThemeView, Self::AccountView],
            Self::ThemeManage => vec![Self::ThemeManage, Self::AccountView],

            Self::ConfigurationsView => vec![Self::ConfigurationsView],
            Self::ConfigurationsManage => {
                vec![Self::ConfigurationsView, Self::ConfigurationsManage]
            }

            Self::ReconSourcesView => vec![
                Self::ReconSourcesView,
                Self::ReconTransactionsView,
                Self::ReconRulesView,
            ],
            Self::ReconSourcesManage => vec![
                Self::ReconSourcesManage,
                Self::ReconSourcesView,
                Self::ReconTransactionsView,
                Self::ReconRulesView,
            ],
            Self::ReconExceptionsView => vec![
                Self::ReconExceptionsView,
                Self::ReconTransactionsView,
                Self::ReconRulesView,
            ],
            Self::ReconExceptionsManage => vec![
                Self::ReconExceptionsManage,
                Self::ReconExceptionsView,
                Self::ReconSourcesView,
                Self::ReconTransactionsView,
                Self::ReconRulesView,
            ],
            Self::ReconTransactionsView => vec![Self::ReconTransactionsView, Self::ReconRulesView],
            Self::ReconTransactionsManage => vec![
                Self::ReconTransactionsManage,
                Self::ReconTransactionsView,
                Self::ReconRulesView,
            ],
            Self::ReconRulesView => vec![Self::ReconRulesView, Self::ReconTransactionsView],
            Self::ReconRulesManage => vec![
                Self::ReconRulesManage,
                Self::ReconRulesView,
                Self::ReconTransactionsView,
            ],
        }
    }

    fn get_role_product_category(&self) -> RoleProductCategory {
        match self {
            // Common across every product — not validated against the merchant's category.
            Self::UsersView | Self::UsersManage => RoleProductCategory::Dashboard,

            // Orchestration-only groups.
            Self::OperationsView
            | Self::OperationsManage
            | Self::ConnectorsView
            | Self::ConnectorsManage
            | Self::WorkflowsView
            | Self::WorkflowsManage
            | Self::AnalyticsView
            | Self::AccountView
            | Self::AccountManage
            | Self::WebhooksView
            | Self::WebhooksManage
            | Self::ApiKeysView
            | Self::ApiKeysManage
            | Self::CloneConnectorManage
            | Self::ThemeView
            | Self::ThemeManage
            | Self::ConfigurationsView
            | Self::ConfigurationsManage => RoleProductCategory::Orchestration,

            // Recon-only groups.
            Self::ReconSourcesView
            | Self::ReconSourcesManage
            | Self::ReconExceptionsView
            | Self::ReconExceptionsManage
            | Self::ReconTransactionsView
            | Self::ReconTransactionsManage
            | Self::ReconRulesView
            | Self::ReconRulesManage => RoleProductCategory::Recon,
        }
    }
}

pub trait ParentGroupExt {
    fn resources(&self) -> Vec<Resource>;
    fn get_descriptions_for_groups(
        entity_type: EntityType,
        groups: Vec<PermissionGroup>,
    ) -> Option<HashMap<ParentGroup, String>>;
    fn get_available_scopes(&self) -> Vec<PermissionScope>;
}

impl ParentGroupExt for ParentGroup {
    fn resources(&self) -> Vec<Resource> {
        match self {
            Self::Operations => OPERATIONS.to_vec(),
            Self::Connectors => CONNECTORS.to_vec(),
            Self::Workflows => WORKFLOWS.to_vec(),
            Self::Analytics => ANALYTICS.to_vec(),
            Self::Users => USERS.to_vec(),
            Self::Account => ACCOUNT.to_vec(),
            Self::Webhook => WEBHOOK.to_vec(),
            Self::ApiKeys => API_KEYS.to_vec(),
            Self::CloneConnector => CLONE_CONNECTOR.to_vec(),
            Self::Theme => THEME.to_vec(),
            Self::Configurations => CONFIGURATIONS.to_vec(),
            Self::ReconSources => RECON_SOURCES.to_vec(),
            Self::ReconExceptions => RECON_EXCEPTIONS.to_vec(),
            Self::ReconTransactions => RECON_TRANSACTIONS.to_vec(),
            Self::ReconRules => RECON_RULES.to_vec(),
        }
    }

    fn get_descriptions_for_groups(
        entity_type: EntityType,
        groups: Vec<PermissionGroup>,
    ) -> Option<HashMap<Self, String>> {
        let descriptions_map = Self::iter()
            .filter_map(|parent| {
                if !groups.iter().any(|group| group.parent() == parent) {
                    return None;
                }
                let filtered_resources =
                    permissions::filter_resources_by_entity_type(parent.resources(), entity_type)?;

                let description = filtered_resources
                    .iter()
                    .map(|res| permissions::get_resource_name(*res, entity_type))
                    .collect::<Option<Vec<_>>>()?
                    .join(", ");

                Some((parent, description))
            })
            .collect::<HashMap<_, _>>();

        descriptions_map
            .is_empty()
            .not()
            .then_some(descriptions_map)
    }

    fn get_available_scopes(&self) -> Vec<PermissionScope> {
        PermissionGroup::iter()
            .filter(|group| group.parent() == *self)
            .map(|group| group.scope())
            .collect()
    }
}

pub static OPERATIONS: [Resource; 8] = [
    Resource::Payment,
    Resource::Refund,
    Resource::Mandate,
    Resource::Dispute,
    Resource::Customer,
    Resource::Payout,
    Resource::Report,
    Resource::Account,
];

pub static CONNECTORS: [Resource; 2] = [Resource::Connector, Resource::Account];

pub static WORKFLOWS: [Resource; 5] = [
    Resource::Routing,
    Resource::ThreeDsDecisionManager,
    Resource::SurchargeDecisionManager,
    Resource::Account,
    Resource::RevenueRecovery,
];

pub static ANALYTICS: [Resource; 3] = [Resource::Analytics, Resource::Report, Resource::Account];

pub static USERS: [Resource; 2] = [Resource::User, Resource::Account];

pub static ACCOUNT: [Resource; 3] = [Resource::Account, Resource::ApiKey, Resource::WebhookEvent];

pub static WEBHOOK: [Resource; 1] = [Resource::WebhookEvent];

pub static API_KEYS: [Resource; 1] = [Resource::ApiKey];

pub static CLONE_CONNECTOR: [Resource; 1] = [Resource::CloneConnector];

pub static THEME: [Resource; 1] = [Resource::Theme];

pub static CONFIGURATIONS: [Resource; 1] = [Resource::SuperpositionConfig];

pub static RECON_SOURCES: [Resource; 3] = [
    Resource::ReconIngestion,
    Resource::ReconTransformation,
    Resource::Account,
];

pub static RECON_EXCEPTIONS: [Resource; 2] = [Resource::ReconException, Resource::Account];

pub static RECON_TRANSACTIONS: [Resource; 3] = [
    Resource::ReconStagingEntry,
    Resource::ReconTransaction,
    Resource::Account,
];

pub static RECON_RULES: [Resource; 2] = [Resource::ReconRule, Resource::Account];
