use std::sync::Arc;

use axum::response::Json;
use serde::Serialize;
use tracing::info;

use crate::data::Histogram;
use crate::features::{ENUM_FEATURE_GROUPS, FEATURE_GROUPS};
use crate::state::AppState;

#[derive(Serialize)]
#[serde(tag = "type")]
pub enum FeatureInfo {
    #[serde(rename = "numeric")]
    Numeric {
        name: String,
        min: f32,
        max: f32,
        step: f32,
        histogram: Histogram,
        description: &'static str,
        detail: &'static str,
        source: &'static str,
    },
    #[serde(rename = "enum")]
    Enum {
        name: String,
        values: Vec<String>,
        description: &'static str,
        detail: &'static str,
        source: &'static str,
    },
}

#[derive(Serialize)]
pub struct FeatureGroupResponse {
    name: String,
    features: Vec<FeatureInfo>,
}

#[derive(Serialize)]
pub struct FeaturesResponse {
    groups: Vec<FeatureGroupResponse>,
}

pub async fn get_features(state: Arc<AppState>) -> Json<FeaturesResponse> {
    // Collect all group names in order, merging numeric and enum groups with the same name
    let mut group_names: Vec<&str> = Vec::new();
    for feature_group in FEATURE_GROUPS {
        if !group_names.contains(&feature_group.name) {
            group_names.push(feature_group.name);
        }
    }
    for enum_group in ENUM_FEATURE_GROUPS {
        if !group_names.contains(&enum_group.name) {
            group_names.push(enum_group.name);
        }
    }

    let mut groups: Vec<FeatureGroupResponse> = Vec::new();

    for &group_name in &group_names {
        let mut features: Vec<FeatureInfo> = Vec::new();

        // Add numeric features for this group
        for feature_group in FEATURE_GROUPS {
            if feature_group.name == group_name {
                for feature_config in feature_group.features {
                    if let Some(feat_idx) = state
                        .data
                        .feature_names
                        .iter()
                        .position(|feat_name| feat_name == feature_config.name)
                    {
                        let stats = &state.data.feature_stats[feat_idx];
                        features.push(FeatureInfo::Numeric {
                            name: feature_config.name.to_string(),
                            min: stats.slider_min,
                            max: stats.slider_max,
                            step: feature_config.step,
                            histogram: stats.histogram.clone(),
                            description: feature_config.description,
                            detail: feature_config.detail,
                            source: feature_config.source,
                        });
                    }
                }
            }
        }

        // Add enum features for this group
        for enum_group in ENUM_FEATURE_GROUPS {
            if enum_group.name == group_name {
                for enum_config in enum_group.features {
                    if let Some(enum_feature) = state
                        .data
                        .enum_features
                        .iter()
                        .find(|enum_feat| enum_feat.name == enum_config.name)
                    {
                        features.push(FeatureInfo::Enum {
                            name: enum_config.name.to_string(),
                            values: enum_feature.values.clone(),
                            description: enum_config.description,
                            detail: enum_config.detail,
                            source: enum_config.source,
                        });
                    }
                }
            }
        }

        if !features.is_empty() {
            groups.push(FeatureGroupResponse {
                name: group_name.to_string(),
                features,
            });
        }
    }

    let num_numeric: usize = groups
        .iter()
        .flat_map(|group| &group.features)
        .filter(|feature| matches!(feature, FeatureInfo::Numeric { .. }))
        .count();
    let num_enum: usize = groups
        .iter()
        .flat_map(|group| &group.features)
        .filter(|feature| matches!(feature, FeatureInfo::Enum { .. }))
        .count();

    info!(
        numeric = num_numeric,
        enums = num_enum,
        groups = groups.len(),
        "GET /api/features"
    );
    Json(FeaturesResponse { groups })
}
