//! Types for SQL visualization DAG generation.
//!
//! The DAG represents the structural decomposition of a SQL query into
//! visual nodes (tables, joins, filters, groups) and edges (data flow).

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

/// Schema for tables: fully-qualified table name → (column_name → data_type).
pub type TableSchema = HashMap<String, HashMap<String, String>>;

/// Result of building a SQL visualization DAG.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DagResult {
    /// Target table name for DML queries (e.g., `"TARGET_TABLE"` for INSERT/CTAS/MERGE).
    /// Empty string for plain SELECT queries.
    #[serde(default, skip_serializing_if = "String::is_empty")]
    pub stmt_name: String,
    /// Adjacency list: parent node → child dependencies.
    pub dependencies: HashMap<String, Vec<String>>,
    /// Node metadata keyed by node identifier.
    pub nodes: HashMap<String, DagNode>,
    /// CTE names found in the query.
    pub ctes: Vec<String>,
}

/// A node in the SQL visualization DAG.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DagNode {
    /// Unique node identifier (e.g., `"Join_3"`, `"users_1"`).
    pub node_id: String,
    /// Node type: `"table"`, `"cte"`, `"final"`, `"UNION"`, `"LEFT_JOIN"`, etc.
    #[serde(rename = "type")]
    pub kind: String,
    /// Hierarchical BFS ID for visualization ordering (e.g., `"0"`, `"00"`, `"01"`).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    /// Node name (table name, CTE alias, subquery alias).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    /// Columns for this node.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub columns: Vec<DagColumn>,
    /// SQL expression text for this node.
    pub expression: String,
    /// Full SQL text (for CTEs and final select).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sql: Option<String>,
    /// JOIN ON clause text.
    #[serde(rename = "on", skip_serializing_if = "Option::is_none")]
    pub on_clause: Option<String>,
    /// JOIN type (e.g., `"INNER"`, `"LEFT"`).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub join_type: Option<String>,
    /// Database/catalog for table nodes.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub db: Option<String>,
    /// Schema for table nodes.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub schema: Option<String>,
}

/// A column reference within a DAG node.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DagColumn {
    /// Column name or alias.
    pub name: String,
    /// SQL expression text for the column.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expression: Option<String>,
    /// Data type (when schema is available).
    #[serde(rename = "datatype", skip_serializing_if = "Option::is_none")]
    pub data_type: Option<String>,
}
