//! Types for the context window optimizer.
//!
//! Provides progressive disclosure of schema information to AI agents,
//! minimizing context window usage while preserving relevant details.

use serde::{Deserialize, Serialize};

/// Configuration for context window optimization.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContextConfig {
    /// Target token budget (approximate). Default: 2000.
    #[serde(default = "default_max_tokens")]
    pub max_tokens: usize,

    /// Disclosure level controlling how much schema detail is exposed.
    #[serde(default)]
    pub level: DisclosureLevel,

    /// Whether to include table/column descriptions.
    #[serde(default = "default_true")]
    pub include_descriptions: bool,

    /// Whether to include column tags (e.g., "pii", "fiscal").
    #[serde(default)]
    pub include_tags: bool,

    /// Whether to include foreign key relationships.
    #[serde(default = "default_true")]
    pub include_foreign_keys: bool,

    /// Tables to always include at full detail regardless of budget.
    #[serde(default)]
    pub priority_tables: Vec<String>,
}

fn default_max_tokens() -> usize {
    2000
}

fn default_true() -> bool {
    true
}

impl Default for ContextConfig {
    fn default() -> Self {
        Self {
            max_tokens: 2000,
            level: DisclosureLevel::default(),
            include_descriptions: true,
            include_tags: false,
            include_foreign_keys: true,
            priority_tables: Vec::new(),
        }
    }
}

/// Progressive disclosure levels for schema information.
///
/// Each level provides increasing detail at the cost of more tokens:
/// - `Fingerprint`: Just a hash for cache checking (~10 tokens)
/// - `TableList`: Table names + descriptions + column counts (~5 tokens/table)
/// - `ColumnNames`: Table names + column names, no types (~20 tokens/table)
/// - `FullRelevant`: Full detail for relevant tables, names only for others
/// - `Full`: Complete schema with all details
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum DisclosureLevel {
    /// Level 0: Only the schema fingerprint hash.
    Fingerprint,
    /// Level 1: Table names, descriptions, and column counts.
    TableList,
    /// Level 2: Table names and column names (no types).
    #[default]
    ColumnNames,
    /// Level 3: Full definitions for relevant tables, names only for others.
    FullRelevant,
    /// Level 4: Complete schema with all details.
    Full,
}

impl DisclosureLevel {
    /// Returns the numeric level (0-4).
    pub fn as_u8(&self) -> u8 {
        match self {
            Self::Fingerprint => 0,
            Self::TableList => 1,
            Self::ColumnNames => 2,
            Self::FullRelevant => 3,
            Self::Full => 4,
        }
    }

    /// Returns the next more-detailed level, or `None` if already at `Full`.
    pub fn next(&self) -> Option<Self> {
        match self {
            Self::Fingerprint => Some(Self::TableList),
            Self::TableList => Some(Self::ColumnNames),
            Self::ColumnNames => Some(Self::FullRelevant),
            Self::FullRelevant => Some(Self::Full),
            Self::Full => None,
        }
    }

    /// Returns the previous less-detailed level, or `None` if already at `Fingerprint`.
    pub fn prev(&self) -> Option<Self> {
        match self {
            Self::Fingerprint => None,
            Self::TableList => Some(Self::Fingerprint),
            Self::ColumnNames => Some(Self::TableList),
            Self::FullRelevant => Some(Self::ColumnNames),
            Self::Full => Some(Self::FullRelevant),
        }
    }
}

impl std::fmt::Display for DisclosureLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Fingerprint => write!(f, "fingerprint"),
            Self::TableList => write!(f, "table_list"),
            Self::ColumnNames => write!(f, "column_names"),
            Self::FullRelevant => write!(f, "full_relevant"),
            Self::Full => write!(f, "full"),
        }
    }
}

/// Result of context window optimization.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContextResult {
    /// The compressed schema representation (YAML or compact text).
    pub compressed_schema: String,

    /// The disclosure level that was actually used.
    pub level_used: DisclosureLevel,

    /// Estimated token count for the compressed output.
    pub estimated_tokens: usize,

    /// Number of tables included in the output.
    pub tables_included: usize,

    /// Total number of tables in the original schema.
    pub tables_total: usize,

    /// Compression ratio (e.g., 0.15 means 85% reduction).
    pub compression_ratio: f64,

    /// Schema fingerprint (SHA-256 hash).
    pub fingerprint: String,
}

/// Natural language summary of a schema.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaSummary {
    /// Human-readable summary of the schema.
    pub natural_language: String,

    /// Per-table summaries.
    pub table_summaries: Vec<TableSummary>,

    /// Estimated token count for the full summary.
    pub estimated_tokens: usize,
}

/// Summary of a single table.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableSummary {
    /// Table name.
    pub name: String,

    /// Table description (from schema metadata or auto-generated).
    pub description: String,

    /// Number of columns.
    pub column_count: usize,

    /// Key columns: primary keys and important columns.
    pub key_columns: Vec<String>,

    /// Relationships in the form "orders.user_id -> users.id".
    pub relationships: Vec<String>,
}
