use std::collections::HashMap;

use log::debug;
use serde_json::{Map, Value};
use superposition_core::experiment::{
    ExperimentConfig, ExperimentGroups, FfiExperimentGroup,
};
use superposition_core::{Experiments, FfiExperiment};
use superposition_sdk::types::{
    ExperimentStatusType as SDKExperimentStatusType, GroupType as SdkGroupType,
};
use superposition_types::database::models::cac::{DependencyGraph, DimensionType};
use superposition_types::database::models::experimentation::{
    Bucket, Buckets, ExperimentStatusType, GroupType, Variant, VariantType, Variants,
};
use superposition_types::{
    Cac, Condition, Config, Context, DimensionInfo, Exp, ExtendedMap, OverrideWithKeys,
    Overrides,
};

use crate::{conversions, types::*};

pub struct ConversionUtils;

impl ConversionUtils {
    pub fn convert_get_config_response(
        response: superposition_sdk::operation::get_config::GetConfigOutput,
    ) -> Result<Config> {
        debug!("Converting get_config response to superposition_types::Config");

        // Convert default configs - these are already Value types
        let default_configs = conversions::hashmap_to_map(response.default_configs);

        // Convert overrides - HashMap<String, HashMap<String, Document>>
        let overrides = response
            .overrides
            .into_iter()
            .map(|(k, v)| {
                let override_values = conversions::hashmap_to_map(v);
                let override_obj = Cac::<Overrides>::try_from(override_values)
                    .map_err(|e| SuperpositionError::SerializationError(e.to_string()))?;
                Ok((k, override_obj.into_inner()))
            })
            .collect::<Result<HashMap<String, Overrides>>>()?;

        // Convert contexts - Vec<ContextPartial>
        let contexts = response
            .contexts
            .into_iter()
            .map(|context_partial| {
                // Convert condition Document to Map<String, Value>
                let condition_map =
                    conversions::hashmap_to_map(context_partial.condition);

                // Create Condition directly from Map<String, Value>
                let condition =
                    Cac::<Condition>::try_from(condition_map).map_err(|e| {
                        SuperpositionError::SerializationError(format!(
                            "Invalid condition: {}",
                            e
                        ))
                    })?;

                let override_with_keys =
                    OverrideWithKeys::try_from(context_partial.override_with_keys)
                        .map_err(|e| {
                            SuperpositionError::SerializationError(format!(
                                "Invalid override_with_keys: {e}",
                            ))
                        })?;

                Ok(Context {
                    id: context_partial.id,
                    condition: condition.into_inner(),
                    priority: context_partial.priority,
                    weight: context_partial.weight,
                    override_with_keys,
                })
            })
            .collect::<Result<Vec<Context>>>()?;

        let dimensions = response
            .dimensions
            .into_iter()
            .map(|(key, dimension_info)| {
                let schema = conversions::hashmap_to_map(dimension_info.schema);
                let dim_info = DimensionInfo {
                    schema: ExtendedMap::from(schema),
                    position: dimension_info.position,
                    dimension_type: Self::try_dimension_type(
                        dimension_info.dimension_type,
                    )?,
                    dependency_graph: DependencyGraph(dimension_info.dependency_graph),
                    value_compute_function_name: dimension_info
                        .value_compute_function_name,
                };
                Ok((key, dim_info))
            })
            .collect::<Result<HashMap<String, DimensionInfo>>>()?;

        let config = Config {
            contexts,
            overrides,
            default_configs: default_configs.into(),
            dimensions,
        };

        debug!("Successfully converted config with {} contexts, {} overrides, {} default configs", 
               config.contexts.len(), config.overrides.len(), config.default_configs.len());

        Ok(config)
    }

    fn try_dimension_type(
        dim_type: superposition_sdk::types::DimensionType,
    ) -> Result<DimensionType> {
        match dim_type {
            superposition_sdk::types::DimensionType::RemoteCohort(cohort_based_on) => {
                Ok(DimensionType::RemoteCohort(cohort_based_on))
            }
            superposition_sdk::types::DimensionType::LocalCohort(cohort_based_on) => {
                Ok(DimensionType::LocalCohort(cohort_based_on))
            }
            superposition_sdk::types::DimensionType::Regular => {
                Ok(DimensionType::Regular {})
            }
            _ => Err(SuperpositionError::SerializationError(
                "Unknown dimension type".to_string(),
            )),
        }
    }

    pub fn convert_value_to_config(map: &Map<String, Value>) -> Result<Config> {
        // Extract contexts array
        let contexts =
            map.get("contexts")
                .and_then(|v| v.as_array())
                .ok_or_else(|| {
                    SuperpositionError::ConfigError(
                        "Missing or invalid 'contexts' field".to_string(),
                    )
                })?;

        let parsed_contexts: Result<Vec<Context>> = contexts
            .iter()
            .map(|context_val| {
                let context_obj = context_val.as_object().ok_or_else(|| {
                    SuperpositionError::ConfigError(
                        "Context must be an object".to_string(),
                    )
                })?;

                // Extract required fields
                let id = context_obj
                    .get("id")
                    .and_then(|v| v.as_str())
                    .ok_or_else(|| {
                        SuperpositionError::ConfigError(
                            "Missing or invalid 'id' field in context".to_string(),
                        )
                    })?
                    .to_string();

                let priority = context_obj
                    .get("priority")
                    .and_then(|v| v.as_i64())
                    .ok_or_else(|| {
                        SuperpositionError::ConfigError(
                            "Missing or invalid 'priority' field in context".to_string(),
                        )
                    })? as i32;

                let weight = context_obj
                    .get("weight")
                    .and_then(|v| v.as_i64())
                    .ok_or_else(|| {
                        SuperpositionError::ConfigError(
                            "Missing or invalid 'weight' field in context".to_string(),
                        )
                    })? as i32;

                let override_with_keys: Vec<String> = context_obj
                    .get("override_with_keys")
                    .and_then(|v| v.as_array())
                    .ok_or_else(|| {
                        SuperpositionError::ConfigError(
                            "Missing or invalid 'override_with_keys' field in context"
                                .to_string(),
                        )
                    })?
                    .iter()
                    .filter_map(|v| v.as_str().map(String::from))
                    .collect();
                let override_with_keys = OverrideWithKeys::try_from(override_with_keys)
                    .map_err(|e| {
                    SuperpositionError::ConfigError(format!(
                        "Invalid override_with_keys: {e}",
                    ))
                })?;

                // Extract condition
                let condition_map = context_obj
                    .get("condition")
                    .and_then(|v| v.as_object())
                    .ok_or_else(|| {
                        SuperpositionError::ConfigError(
                            "Missing or invalid 'condition' field in context".to_string(),
                        )
                    })?
                    .clone();

                let condition = Cac::<Condition>::try_from(condition_map)
                    .map_err(|e| {
                        SuperpositionError::SerializationError(format!(
                            "Invalid condition: {}",
                            e
                        ))
                    })?
                    .into_inner();

                Ok(Context {
                    id,
                    condition,
                    priority,
                    weight,
                    override_with_keys,
                })
            })
            .collect();

        let contexts = parsed_contexts?;

        // Extract overrides object
        let overrides_obj = map
            .get("overrides")
            .and_then(|v| v.as_object())
            .ok_or_else(|| {
                SuperpositionError::ConfigError(
                    "Missing or invalid 'overrides' field".to_string(),
                )
            })?;

        let mut overrides: HashMap<String, Overrides> = HashMap::new();
        for (key, value) in overrides_obj {
            let override_map = value
                .as_object()
                .ok_or_else(|| {
                    SuperpositionError::ConfigError(format!(
                        "Override '{}' must be an object",
                        key
                    ))
                })?
                .clone();

            let override_obj = Cac::<Overrides>::try_from(override_map)
                .map_err(|e| {
                    SuperpositionError::SerializationError(format!(
                        "Invalid override '{}': {}",
                        key, e
                    ))
                })?
                .into_inner();

            overrides.insert(key.clone(), override_obj);
        }

        // Extract default_configs object
        let default_configs = map
            .get("default_configs")
            .and_then(|v| v.as_object())
            .ok_or_else(|| {
                SuperpositionError::ConfigError(
                    "Missing or invalid 'default_configs' field".to_string(),
                )
            })?
            .clone();

        let dimensions = map
            .get("dimensions")
            .and_then(|v| v.as_object())
            .map(|dim| {
                dim.iter()
                    .map(|(key, value)| {
                        let dim_info: DimensionInfo =
                            serde_json::from_value(value.clone()).map_err(|e| {
                                SuperpositionError::SerializationError(format!(
                                    "Invalid dimension info for '{}': {}",
                                    key, e
                                ))
                            })?;
                        Ok((key.clone(), dim_info))
                    })
                    .collect::<Result<HashMap<String, DimensionInfo>>>()
            })
            .unwrap_or_else(|| Ok(HashMap::new()))?;

        Ok(Config {
            contexts,
            overrides,
            default_configs: default_configs.into(),
            dimensions,
        })
    }

    /// Convert list_experiment SDK response to structured experiment data
    pub fn convert_experiments_response(
        response: Vec<superposition_sdk::types::ExperimentResponse>,
    ) -> Result<Experiments> {
        debug!("Converting experiments response");

        let mut trimmed_exp_list: Experiments = Vec::new();

        for exp in response {
            // Convert experiment context (condition)
            let condition_map = conversions::hashmap_to_map(exp.context);

            // Convert variants
            let mut variants: Variants = Variants::new(vec![]);
            for variant in exp.variants {
                let variant_type = match variant.variant_type {
                    superposition_sdk::types::VariantType::Control => {
                        VariantType::CONTROL
                    }
                    superposition_sdk::types::VariantType::Experimental => {
                        VariantType::EXPERIMENTAL
                    }
                    _ => {
                        return Err(SuperpositionError::SerializationError(
                            "Unknown variant type".to_string(),
                        ))
                    }
                };

                // Convert variant overrides - check if overrides exist
                let overrides_map = conversions::hashmap_to_map(variant.overrides);

                let override_ = Exp::<Overrides>::try_from(overrides_map)
                    .map_err(|e| SuperpositionError::SerializationError(e.to_string()))?;

                let variant_value = Variant {
                    id: variant.id,
                    variant_type,
                    context_id: variant.context_id,
                    override_id: variant.override_id,
                    overrides: override_,
                };
                variants.push(variant_value);
            }
            let context = Exp::<Condition>::try_from(condition_map)
                .map_err(|e| {
                    SuperpositionError::SerializationError(format!(
                        "Invalid condition: {}",
                        e
                    ))
                })?
                .into_inner();
            let status = match exp.status {
                SDKExperimentStatusType::Created => ExperimentStatusType::CREATED,
                SDKExperimentStatusType::Inprogress => ExperimentStatusType::INPROGRESS,
                SDKExperimentStatusType::Paused => ExperimentStatusType::PAUSED,
                SDKExperimentStatusType::Concluded => ExperimentStatusType::CONCLUDED,
                SDKExperimentStatusType::Discarded => ExperimentStatusType::DISCARDED,
                _ => {
                    return Err(SuperpositionError::SerializationError(
                        "Unknown experiment status".to_string(),
                    ))
                }
            };
            let experiment = FfiExperiment {
                id: exp.id,
                context,
                variants,
                traffic_percentage: exp.traffic_percentage as u8,
                status,
            };

            trimmed_exp_list.push(experiment);
        }

        Ok(trimmed_exp_list)
    }

    pub fn convert_experiment_groups_response(
        response: Vec<superposition_sdk::types::ExperimentGroupResponse>,
    ) -> Result<ExperimentGroups> {
        debug!("Converting experiment groups response");

        let mut trimmed_group_list: ExperimentGroups = Vec::new();

        for exp_group in response {
            // Convert experiment context (condition)
            let condition_map = conversions::hashmap_to_map(exp_group.context);

            let context = Exp::<Condition>::try_from(condition_map)
                .map_err(|e| {
                    SuperpositionError::SerializationError(format!(
                        "Invalid condition: {}",
                        e
                    ))
                })?
                .into_inner();
            let group_type = match exp_group.group_type {
                SdkGroupType::SystemGenerated => GroupType::SystemGenerated,
                SdkGroupType::UserCreated => GroupType::UserCreated,
                _ => {
                    return Err(SuperpositionError::SerializationError(
                        "Unknown group type".to_string(),
                    ))
                }
            };

            let experiment_group = FfiExperimentGroup {
                id: exp_group.id,
                context,
                traffic_percentage: exp_group.traffic_percentage as u8,
                member_experiment_ids: exp_group.member_experiment_ids,
                group_type,
                buckets: Buckets::try_from(
                    exp_group
                        .buckets
                        .into_iter()
                        .map(|b| {
                            b.map(|bucket| Bucket {
                                variant_id: bucket.variant_id,
                                experiment_id: bucket.experiment_id,
                            })
                        })
                        .collect::<Vec<_>>(),
                )
                .map_err(SuperpositionError::SerializationError)?,
            };

            trimmed_group_list.push(experiment_group);
        }

        Ok(trimmed_group_list)
    }

    pub fn convert_experiment_config_response(
        response: superposition_sdk::operation::get_experiment_config::GetExperimentConfigOutput,
    ) -> Result<ExperimentConfig> {
        debug!("Converting experiment config response");

        let experiments = Self::convert_experiments_response(response.experiments)?;
        let experiment_groups =
            Self::convert_experiment_groups_response(response.experiment_groups)?;

        Ok(ExperimentConfig {
            experiments,
            experiment_groups,
        })
    }

    /// Convert Config back to the legacy format for compatibility with existing provider logic
    pub fn config_to_legacy_format(config: &Config) -> HashMap<String, Value> {
        let mut result = HashMap::new();

        // Convert default_configs
        result.insert(
            "default_configs".to_string(),
            Value::Object((*config.default_configs).clone()),
        );

        // Convert overrides to the expected format
        let mut overrides_map = Map::new();
        for (key, overrides) in &config.overrides {
            let override_value: Map<String, Value> = overrides.clone().into();
            overrides_map.insert(key.clone(), Value::Object(override_value));
        }
        result.insert("overrides".to_string(), Value::Object(overrides_map));

        // Convert contexts
        let contexts_array: Vec<Value> = config
            .contexts
            .iter()
            .map(|context| {
                let condition_map: Map<String, Value> = context.condition.clone().into();
                serde_json::json!({
                    "id": context.id,
                    "priority": context.priority,
                    "weight": context.weight,
                    "override_with_keys": context.override_with_keys,
                    "condition": condition_map
                })
            })
            .collect();
        result.insert("contexts".to_string(), Value::Array(contexts_array));

        debug!(
            "Converted Config to legacy format with {} sections",
            result.len()
        );
        result
    }

    /// Evaluate config using superposition_types logic and return resolved values
    pub fn evaluate_config(
        config: Config,
        dimension_data: &Map<String, Value>,
        prefix_filter: Option<&[String]>,
    ) -> Result<HashMap<String, Value>> {
        debug!(
            "Evaluating config with dimension data: {:?}",
            dimension_data.keys().collect::<Vec<_>>()
        );

        // Filter by dimensions first
        let filtered_config = config.filter_by_dimensions(dimension_data);
        debug!(
            "Filtered config has {} contexts after dimension filtering",
            filtered_config.contexts.len()
        );

        // Apply prefix filtering if specified
        let final_config = if let Some(prefixes) = prefix_filter {
            let prefix_set: std::collections::HashSet<String> =
                prefixes.iter().cloned().collect();
            filtered_config.filter_by_prefix(&prefix_set)
        } else {
            filtered_config
        };

        debug!(
            "Final config has {} contexts after prefix filtering",
            final_config.contexts.len()
        );

        // Start with default configs
        let mut result = final_config.default_configs.into_inner();

        // Apply overrides based on context priority (higher priority wins)
        let mut sorted_contexts = final_config.contexts.clone();
        sorted_contexts.sort_by_key(|c| std::cmp::Reverse(c.priority)); // Sort by priority descending

        for context in sorted_contexts {
            if let Some(override_key) = context.override_with_keys.first() {
                if let Some(overrides) = final_config.overrides.get(override_key) {
                    let override_map: Map<String, Value> = overrides.clone().into();
                    for (override_key, value) in override_map {
                        result.insert(override_key, value);
                        debug!("Applied override for key");
                    }
                }
            }
        }

        debug!(
            "Config evaluation completed with {} final keys",
            result.len()
        );

        // Convert Map<String, Value> to HashMap<String, Value>
        let final_result: HashMap<String, Value> = result.into_iter().collect();
        Ok(final_result)
    }
}
