use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;

/// Source stage configuration for Athena pipelines.
///
/// This mirrors the gateway fetch shape closely enough for the runtime to
/// synthesize an internal `/gateway/fetch` request body.
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case", default)]
pub struct SourceConfig {
    pub table_name: String,
    pub view_name: Option<String>,
    pub columns: Option<Vec<String>>,
    pub conditions: Option<Vec<ConditionEntry>>,
    pub limit: Option<u64>,
    pub client: Option<String>,
}

/// A single equality condition in pipeline source configuration.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct ConditionEntry {
    pub eq_column: String,
    #[serde(deserialize_with = "deserialize_eq_value")]
    pub eq_value: String,
}

fn deserialize_eq_value<'de, D>(deserializer: D) -> Result<String, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let value: Value = Value::deserialize(deserializer)?;
    Ok(match value {
        Value::Bool(flag) => {
            if flag {
                "true".to_string()
            } else {
                "false".to_string()
            }
        }
        Value::String(text) => text,
        Value::Number(number) => number.to_string(),
        other => other.to_string(),
    })
}

/// Transform-stage configuration for grouped or cumulative fetch post-processing.
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case", default)]
pub struct TransformConfig {
    pub group_by: Option<String>,
    pub time_granularity: Option<String>,
    pub aggregation_column: Option<String>,
    pub aggregation_strategy: Option<String>,
    pub aggregation_dedup: Option<bool>,
    pub column_aliases: Option<HashMap<String, String>>,
}

/// Sink-stage configuration for Athena pipelines.
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case", default)]
pub struct SinkConfig {
    pub table_name: String,
    pub client: Option<String>,
}

/// Full declarative pipeline definition used by templates and request merging.
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case", default)]
pub struct PipelineDefinition {
    pub source: SourceConfig,
    pub transform: Option<TransformConfig>,
    pub sink: SinkConfig,
}

/// Pipeline execution request with an optional named template and inline overrides.
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case", default)]
pub struct PipelineRequest {
    /// Reference a prebuilt pipeline by name.
    pub pipeline: Option<String>,
    pub source: Option<SourceConfig>,
    pub transform: Option<TransformConfig>,
    pub sink: Option<SinkConfig>,
    pub dry_run: Option<bool>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn condition_entry_coerces_non_string_eq_values() {
        let condition: ConditionEntry = serde_json::from_value(json!({
            "eq_column": "enabled",
            "eq_value": true
        }))
        .expect("condition should parse");

        assert_eq!(condition.eq_value, "true");
    }
}
