use std::collections::HashMap;

use serde_json::json;

use super::grants::{
    GrantDescriptor, merge_permissions, normalize_grant_inputs, permissions_map_from_grant_keys,
    right_modules_for_grant_keys,
};

const API_KEY_INTERNAL_METADATA_KEY: &str = "_athenaAuth";
const API_KEY_GRANT_KEYS_METADATA_KEY: &str = "grantKeys";
const API_KEY_RIGHT_MODULES_METADATA_KEY: &str = "rightModules";

pub(crate) fn normalize_api_key_permissions_input(
    permissions: Option<&serde_json::Value>,
    grant_keys: Option<&Vec<String>>,
    right_keys: Option<&Vec<String>>,
    right_modules: Option<&Vec<athena_auth_core::NativeRightModuleInput>>,
    grant_catalog: &[GrantDescriptor],
) -> Option<serde_json::Value> {
    let normalized_keys = normalize_grant_inputs(
        grant_keys.into_iter().flatten().map(String::as_str),
        right_keys.into_iter().flatten().map(String::as_str),
        right_modules.into_iter().flatten(),
    );

    if normalized_keys.is_empty() {
        return permissions.cloned();
    }

    let mut effective = HashMap::<String, Vec<String>>::new();

    if let Some(map) = permissions.and_then(|value| value.as_object()) {
        for (resource, actions) in map {
            let entries = actions
                .as_array()
                .into_iter()
                .flatten()
                .filter_map(|value| value.as_str().map(ToString::to_string))
                .collect::<Vec<_>>();

            if !entries.is_empty() {
                effective.insert(resource.clone(), entries);
            }
        }
    }

    merge_permissions(
        &mut effective,
        permissions_map_from_grant_keys(normalized_keys.iter().map(String::as_str), grant_catalog),
    );

    if effective.is_empty() {
        None
    } else {
        Some(json!(effective))
    }
}

pub(crate) fn normalize_api_key_grant_keys_input(
    grant_keys: Option<&Vec<String>>,
    right_keys: Option<&Vec<String>>,
    right_modules: Option<&Vec<athena_auth_core::NativeRightModuleInput>>,
) -> Option<Vec<String>> {
    let normalized = normalize_grant_inputs(
        grant_keys.into_iter().flatten().map(String::as_str),
        right_keys.into_iter().flatten().map(String::as_str),
        right_modules.into_iter().flatten(),
    );

    if normalized.is_empty() {
        None
    } else {
        Some(normalized)
    }
}

fn stored_metadata_object(
    raw_metadata: Option<&str>,
) -> serde_json::Map<String, serde_json::Value> {
    raw_metadata
        .and_then(|value| serde_json::from_str::<serde_json::Value>(value).ok())
        .and_then(|value| value.as_object().cloned())
        .unwrap_or_default()
}

pub(crate) fn stored_api_key_grant_keys(raw_metadata: Option<&str>) -> Vec<String> {
    normalize_api_key_grant_keys_input(
        stored_metadata_object(raw_metadata)
            .get(API_KEY_INTERNAL_METADATA_KEY)
            .and_then(|value| value.as_object())
            .and_then(|value| value.get(API_KEY_GRANT_KEYS_METADATA_KEY))
            .and_then(|value| value.as_array())
            .map(|items| {
                items
                    .iter()
                    .filter_map(|value| value.as_str().map(ToString::to_string))
                    .collect::<Vec<_>>()
            })
            .as_ref(),
        None,
        None,
    )
    .unwrap_or_default()
}

pub(crate) fn stored_api_key_right_modules(
    raw_metadata: Option<&str>,
) -> Vec<athena_auth_core::NativeRightModuleInput> {
    let grant_keys = stored_api_key_grant_keys(raw_metadata);
    let stored_modules = stored_metadata_object(raw_metadata)
        .get(API_KEY_INTERNAL_METADATA_KEY)
        .and_then(|value| value.as_object())
        .and_then(|value| value.get(API_KEY_RIGHT_MODULES_METADATA_KEY))
        .cloned()
        .and_then(|value| {
            serde_json::from_value::<Vec<athena_auth_core::NativeRightModuleInput>>(value).ok()
        });

    if let Some(modules) = stored_modules.filter(|modules| !modules.is_empty()) {
        modules
    } else {
        right_modules_for_grant_keys(
            grant_keys.iter().map(String::as_str),
            &athena_rights::native_right_catalog(),
        )
    }
}

pub(crate) fn stored_api_key_user_metadata(
    raw_metadata: Option<&str>,
) -> Option<serde_json::Value> {
    let mut metadata = stored_metadata_object(raw_metadata);
    metadata.remove(API_KEY_INTERNAL_METADATA_KEY);
    if metadata.is_empty() {
        None
    } else {
        Some(serde_json::Value::Object(metadata))
    }
}

pub(crate) fn build_stored_api_key_metadata(
    raw_metadata: Option<&str>,
    metadata: Option<&serde_json::Value>,
    grant_keys: Option<&Vec<String>>,
    right_keys: Option<&Vec<String>>,
    right_modules: Option<&Vec<athena_auth_core::NativeRightModuleInput>>,
) -> Option<String> {
    let mut effective = stored_metadata_object(raw_metadata);

    if let Some(metadata) = metadata {
        effective = metadata.as_object().cloned().unwrap_or_default();
    }

    match normalize_api_key_grant_keys_input(grant_keys, right_keys, right_modules) {
        Some(grant_keys) => {
            effective.insert(
                API_KEY_INTERNAL_METADATA_KEY.to_string(),
                json!({
                    API_KEY_GRANT_KEYS_METADATA_KEY: grant_keys,
                    API_KEY_RIGHT_MODULES_METADATA_KEY: right_modules.cloned().unwrap_or_default(),
                }),
            );
        }
        None => {
            effective.remove(API_KEY_INTERNAL_METADATA_KEY);
        }
    }

    if effective.is_empty() {
        None
    } else {
        Some(serde_json::to_string(&serde_json::Value::Object(effective)).unwrap_or_default())
    }
}
