use crate::cube_bridge::case_variant::CaseVariant;
use crate::cube_bridge::member_sql::MemberSql;
use crate::test_fixtures::cube_bridge::yaml::case::YamlCaseVariant;
use crate::test_fixtures::cube_bridge::yaml::mask::YamlMask;
use crate::test_fixtures::cube_bridge::yaml::measure::YamlMultiStageFilter;
use crate::test_fixtures::cube_bridge::yaml::timeshift::YamlTimeShiftDefinition;
use crate::test_fixtures::cube_bridge::{
    MockDimensionDefinition, MockGranularityDefinition, MockMemberSql,
};
use serde::Deserialize;
use std::rc::Rc;

#[derive(Debug, Deserialize)]
pub struct YamlGranularityEntry {
    pub name: String,
    #[serde(default)]
    pub interval: Option<String>,
    #[serde(default)]
    pub origin: Option<String>,
    #[serde(default)]
    pub offset: Option<String>,
    #[serde(default)]
    pub sql: Option<String>,
}

pub struct YamlDimensionBuildResult {
    pub definition: MockDimensionDefinition,
    pub granularities: Vec<(String, MockGranularityDefinition)>,
}

#[derive(Debug, Deserialize)]
pub struct YamlDimensionDefinition {
    #[serde(rename = "type")]
    dimension_type: String,
    #[serde(default)]
    multi_stage: Option<bool>,
    #[serde(default, rename = "add_group_by")]
    add_group_by_references: Option<Vec<String>>,
    #[serde(default)]
    sub_query: Option<bool>,
    #[serde(default)]
    propagate_filters_to_sub_query: Option<bool>,
    #[serde(default)]
    values: Option<Vec<String>>,
    #[serde(default)]
    primary_key: Option<bool>,
    #[serde(default)]
    sql: Option<String>,
    #[serde(default)]
    case: Option<YamlCaseVariant>,
    #[serde(default)]
    latitude: Option<String>,
    #[serde(default)]
    longitude: Option<String>,
    #[serde(default)]
    time_shift: Vec<YamlTimeShiftDefinition>,
    #[serde(default)]
    granularities: Vec<YamlGranularityEntry>,
    #[serde(default)]
    filter: Option<YamlMultiStageFilter>,
    #[serde(default)]
    mask: Option<YamlMask>,
}

impl YamlDimensionDefinition {
    pub fn build(self) -> YamlDimensionBuildResult {
        let time_shift = if !self.time_shift.is_empty() {
            Some(self.time_shift.into_iter().map(|ts| ts.build()).collect())
        } else {
            None
        };

        let case = self.case.map(|cv| match cv {
            YamlCaseVariant::Case(case_def) => Rc::new(CaseVariant::Case(case_def.build())),
            YamlCaseVariant::CaseSwitch(switch_def) => {
                Rc::new(CaseVariant::CaseSwitch(switch_def.build()))
            }
        });

        const PREDEFINED: &[&str] = &[
            "second", "minute", "hour", "day", "week", "month", "quarter", "year",
        ];

        let granularities: Vec<(String, MockGranularityDefinition)> = self
            .granularities
            .into_iter()
            .map(|entry| {
                let interval = entry.interval.unwrap_or_else(|| {
                    if PREDEFINED.contains(&entry.name.as_str()) {
                        format!("1 {}", entry.name)
                    } else {
                        panic!("Granularity '{}' requires explicit interval", entry.name)
                    }
                });

                let def = if let Some(sql_str) = entry.sql {
                    let sql: Rc<dyn MemberSql> = Rc::new(MockMemberSql::new(&sql_str).unwrap());
                    MockGranularityDefinition::builder()
                        .interval(interval)
                        .origin_opt(entry.origin)
                        .offset_opt(entry.offset)
                        .sql(sql)
                        .build()
                } else {
                    MockGranularityDefinition::builder()
                        .interval(interval)
                        .origin_opt(entry.origin)
                        .offset_opt(entry.offset)
                        .build()
                };

                (entry.name, def)
            })
            .collect();

        let filter = self.filter.map(|f| f.build(None));

        let definition = MockDimensionDefinition::builder()
            .dimension_type(self.dimension_type)
            .multi_stage(self.multi_stage)
            .add_group_by_references(self.add_group_by_references)
            .sub_query(self.sub_query)
            .propagate_filters_to_sub_query(self.propagate_filters_to_sub_query)
            .values(self.values)
            .primary_key(self.primary_key)
            .sql_opt(self.sql)
            .case(case)
            .latitude_opt(self.latitude)
            .longitude_opt(self.longitude)
            .time_shift(time_shift)
            .filter(filter)
            .resolved_mask_sql_opt(self.mask.map(|m| m.to_sql_string()))
            .build();

        YamlDimensionBuildResult {
            definition,
            granularities,
        }
    }
}
