use athena_auth_core::ResolvedGrantAssignment;
pub use athena_auth_core::{
    GrantDescriptor, NativeRightModuleInput, grant_by_key, merge_permissions,
    permissions_map_from_assignments, permissions_map_from_grant_keys, resolve_grant_assignments,
};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GrantCatalogEntry {
    #[serde(flatten)]
    pub grant: GrantDescriptor,
    #[serde(rename = "nativeRight", skip_serializing_if = "Option::is_none")]
    pub native_right: Option<athena_rights::AthenaRightDescriptor>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeRightCatalogEntry {
    #[serde(rename = "nativeRight")]
    pub native_right: athena_rights::AthenaRightDescriptor,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub grant: Option<GrantDescriptor>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResolvedGrantAssignmentView {
    #[serde(flatten)]
    pub resolved: ResolvedGrantAssignment,
    #[serde(rename = "rightKey")]
    pub right_key: String,
    #[serde(rename = "nativeRight", skip_serializing_if = "Option::is_none")]
    pub native_right: Option<athena_rights::AthenaRightDescriptor>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResolvedNativeRightAssignmentView {
    #[serde(rename = "nativeRight")]
    pub native_right: athena_rights::AthenaRightDescriptor,
    #[serde(rename = "rightKey")]
    pub right_key: String,
    #[serde(rename = "subjectKind")]
    pub subject_kind: String,
    #[serde(rename = "subjectId")]
    pub subject_id: String,
    #[serde(rename = "sourceKind")]
    pub source_kind: String,
    #[serde(rename = "sourceRef")]
    pub source_ref: String,
    pub metadata: serde_json::Value,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub grant: Option<GrantDescriptor>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NativeRightAssignmentView {
    #[serde(rename = "nativeRight")]
    pub native_right: athena_rights::AthenaRightDescriptor,
    #[serde(rename = "rightKey")]
    pub right_key: String,
    #[serde(rename = "grantKey")]
    pub grant_key: String,
    #[serde(rename = "subjectKind")]
    pub subject_kind: String,
    #[serde(rename = "subjectId")]
    pub subject_id: String,
    #[serde(rename = "sourceKind")]
    pub source_kind: String,
    #[serde(rename = "sourceRef")]
    pub source_ref: String,
    pub metadata: serde_json::Value,
}

pub fn native_right_by_key<'a>(
    catalog: &'a [athena_rights::AthenaRightDescriptor],
    key: &str,
) -> Option<&'a athena_rights::AthenaRightDescriptor> {
    catalog.iter().find(|descriptor| descriptor.key == key)
}

pub fn merged_native_right_catalog(
    extra_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<athena_rights::AthenaRightDescriptor> {
    let mut merged = BTreeMap::<String, athena_rights::AthenaRightDescriptor>::from_iter(
        athena_rights::native_right_catalog()
            .into_iter()
            .map(|descriptor| (descriptor.key.clone(), descriptor)),
    );

    for descriptor in extra_catalog {
        merged.insert(descriptor.key.clone(), descriptor.clone());
    }

    merged.into_values().collect()
}

pub fn grant_catalog_from_native_rights(
    rights: impl IntoIterator<Item = athena_rights::AthenaRightDescriptor>,
) -> Vec<GrantDescriptor> {
    rights
        .into_iter()
        .map(|right| GrantDescriptor {
            key: right.key,
            resource: right.resource,
            action: right.action,
            description: right.description,
        })
        .collect()
}

pub fn merged_grant_catalog(
    grant_catalog: &[GrantDescriptor],
    extra_native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<GrantDescriptor> {
    let mut merged = BTreeMap::<String, GrantDescriptor>::from_iter(
        grant_catalog_from_native_rights(merged_native_right_catalog(extra_native_right_catalog))
            .into_iter()
            .map(|descriptor| (descriptor.key.clone(), descriptor)),
    );

    for descriptor in grant_catalog {
        merged.insert(descriptor.key.clone(), descriptor.clone());
    }

    merged.into_values().collect()
}

pub fn grant_catalog_entries(
    grant_catalog: &[GrantDescriptor],
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<GrantCatalogEntry> {
    grant_catalog
        .iter()
        .cloned()
        .map(|grant| GrantCatalogEntry {
            native_right: native_right_by_key(native_right_catalog, &grant.key).cloned(),
            grant,
        })
        .collect()
}

pub fn native_right_catalog_entries(
    grant_catalog: &[GrantDescriptor],
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<NativeRightCatalogEntry> {
    merged_native_right_catalog(native_right_catalog)
        .into_iter()
        .map(|native_right| NativeRightCatalogEntry {
            grant: grant_by_key(grant_catalog, &native_right.key).cloned(),
            native_right,
        })
        .collect()
}

pub fn native_rights_for_grant_keys<'a>(
    grant_keys: impl IntoIterator<Item = &'a str>,
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<athena_rights::AthenaRightDescriptor> {
    let mut resolved = BTreeMap::<String, athena_rights::AthenaRightDescriptor>::new();

    for grant_key in grant_keys {
        let Some(native_right) = native_right_by_key(native_right_catalog, grant_key) else {
            continue;
        };
        resolved.insert(native_right.key.clone(), native_right.clone());
    }

    resolved.into_values().collect()
}

pub fn native_right_modules_for_grant_keys<'a>(
    grant_keys: impl IntoIterator<Item = &'a str>,
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<athena_rights::AthenaRightModule> {
    athena_rights::resolve_right_module_selection(grant_keys, native_right_catalog).modules
}

pub fn right_module_inputs_from_native_modules(
    modules: &[athena_rights::AthenaRightModule],
) -> Vec<NativeRightModuleInput> {
    modules
        .iter()
        .map(|module| NativeRightModuleInput {
            key: module.key.clone(),
            right_keys: module
                .rights
                .iter()
                .map(|right| right.key.clone())
                .collect(),
        })
        .collect()
}

pub fn right_modules_for_grant_keys<'a>(
    grant_keys: impl IntoIterator<Item = &'a str>,
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<NativeRightModuleInput> {
    right_module_inputs_from_native_modules(&native_right_modules_for_grant_keys(
        grant_keys,
        native_right_catalog,
    ))
}

pub fn native_right_keys_for_grant_keys<'a>(
    grant_keys: impl IntoIterator<Item = &'a str>,
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<String> {
    grant_keys
        .into_iter()
        .filter_map(|grant_key| native_right_by_key(native_right_catalog, grant_key))
        .map(|native_right| native_right.key.clone())
        .collect::<BTreeSet<_>>()
        .into_iter()
        .collect()
}

pub fn unknown_native_right_keys_for_grant_keys<'a>(
    grant_keys: impl IntoIterator<Item = &'a str>,
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<String> {
    grant_keys
        .into_iter()
        .filter(|grant_key| native_right_by_key(native_right_catalog, grant_key).is_none())
        .map(ToString::to_string)
        .collect::<BTreeSet<_>>()
        .into_iter()
        .collect()
}

pub fn normalize_grant_key_inputs<'a>(
    grant_keys: impl IntoIterator<Item = &'a str>,
    right_keys: impl IntoIterator<Item = &'a str>,
) -> Vec<String> {
    grant_keys
        .into_iter()
        .chain(right_keys)
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(ToString::to_string)
        .collect::<BTreeSet<_>>()
        .into_iter()
        .collect()
}

pub fn normalize_grant_module_inputs<'a>(
    right_modules: impl IntoIterator<Item = &'a NativeRightModuleInput>,
) -> Vec<String> {
    right_modules
        .into_iter()
        .flat_map(|module| module.right_keys.iter().map(String::as_str))
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(ToString::to_string)
        .collect::<BTreeSet<_>>()
        .into_iter()
        .collect()
}

pub fn normalize_grant_inputs<'a>(
    grant_keys: impl IntoIterator<Item = &'a str>,
    right_keys: impl IntoIterator<Item = &'a str>,
    right_modules: impl IntoIterator<Item = &'a NativeRightModuleInput>,
) -> Vec<String> {
    grant_keys
        .into_iter()
        .chain(right_keys)
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(ToString::to_string)
        .chain(normalize_grant_module_inputs(right_modules))
        .collect::<BTreeSet<_>>()
        .into_iter()
        .collect()
}

pub fn grant_keys_from_assignments<'a>(
    assignments: impl IntoIterator<Item = &'a athena_auth_core::GrantAssignment>,
) -> Vec<String> {
    assignments
        .into_iter()
        .map(|assignment| assignment.grant_key.as_str())
        .collect::<BTreeSet<_>>()
        .into_iter()
        .map(ToString::to_string)
        .collect()
}

pub fn resolved_grant_assignment_views(
    resolved_grants: Vec<ResolvedGrantAssignment>,
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<ResolvedGrantAssignmentView> {
    resolved_grants
        .into_iter()
        .map(|resolved| ResolvedGrantAssignmentView {
            right_key: resolved.grant_key.clone(),
            native_right: native_right_by_key(native_right_catalog, &resolved.grant_key).cloned(),
            resolved,
        })
        .collect()
}

pub fn resolved_native_right_assignment_views<'a>(
    assignments: impl IntoIterator<Item = &'a athena_auth_core::GrantAssignment>,
    grant_catalog: &[GrantDescriptor],
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<ResolvedNativeRightAssignmentView> {
    assignments
        .into_iter()
        .filter_map(|assignment| {
            let native_right =
                native_right_by_key(native_right_catalog, &assignment.grant_key)?.clone();
            Some(ResolvedNativeRightAssignmentView {
                grant: grant_by_key(grant_catalog, &assignment.grant_key).cloned(),
                right_key: assignment.grant_key.clone(),
                subject_kind: assignment.subject_kind.clone(),
                subject_id: assignment.subject_id.clone(),
                source_kind: assignment.source_kind.clone(),
                source_ref: assignment.source_ref.clone(),
                metadata: assignment.metadata.clone(),
                native_right,
            })
        })
        .collect()
}

pub fn native_right_assignment_views<'a>(
    assignments: impl IntoIterator<Item = &'a athena_auth_core::GrantAssignment>,
    native_right_catalog: &[athena_rights::AthenaRightDescriptor],
) -> Vec<NativeRightAssignmentView> {
    assignments
        .into_iter()
        .filter_map(|assignment| {
            let native_right =
                native_right_by_key(native_right_catalog, &assignment.grant_key)?.clone();
            Some(NativeRightAssignmentView {
                native_right,
                right_key: assignment.grant_key.clone(),
                grant_key: assignment.grant_key.clone(),
                subject_kind: assignment.subject_kind.clone(),
                subject_id: assignment.subject_id.clone(),
                source_kind: assignment.source_kind.clone(),
                source_ref: assignment.source_ref.clone(),
                metadata: assignment.metadata.clone(),
            })
        })
        .collect()
}
