"""
Database schema definitions for FactorDB tables
"""

from typing import Dict, List

# Schema for A1_factor_basic table
FACTOR_BASIC_SCHEMA = {
    "table_name": "A1_factor_basic",
    "columns": [
        ("factor_id", "String"),
        ("name", "Nullable(String)"),
        ("expression", "Nullable(String)"),
        ("explanation", "Nullable(String)"),
        ("other_info", "Nullable(String)"),
        ("register_datetime", "DateTime"),
    ],
    "engine": "MergeTree()",
    "order_by": "factor_id",
    "primary_key": "factor_id",
}

# Evaluation metric columns - all metrics from project_statement.md
EVALUATION_METRICS = [
    # Full period IC metrics
    ("ic_mean", "Nullable(Float32)"),
    ("rank_ic_mean", "Nullable(Float32)"),
    ("ic_ir", "Nullable(Float32)"),
    ("ic_t_stat", "Nullable(Float32)"),
    ("direction", "Nullable(Int8)"),

    # 5-year IC metrics
    ("ic_mean_5y", "Nullable(Float32)"),
    ("rank_ic_5y", "Nullable(Float32)"),
    ("ic_ir_5y", "Nullable(Float32)"),
    ("ic_t_stat_5y", "Nullable(Float32)"),
    ("direction_5y", "Nullable(Int8)"),

    # 1-year IC metrics
    ("ic_mean_1y", "Nullable(Float32)"),
    ("rank_ic_1y", "Nullable(Float32)"),
    ("ic_ir_1y", "Nullable(Float32)"),
    ("ic_t_stat_1y", "Nullable(Float32)"),
    ("direction_1y", "Nullable(Int8)"),

    # Monotonicity (5 groups)
    ("is_mono_5", "Nullable(Int8)"),
    ("is_mono_5_5y", "Nullable(Int8)"),
    ("is_mono_5_1y", "Nullable(Int8)"),

    # Monotonicity (10 groups)
    ("is_mono_10", "Nullable(Int8)"),
    ("is_mono_10_5y", "Nullable(Int8)"),
    ("is_mono_10_1y", "Nullable(Int8)"),

    # Monotonicity (15 groups)
    ("is_mono_15", "Nullable(Int8)"),
    ("is_mono_15_5y", "Nullable(Int8)"),
    ("is_mono_15_1y", "Nullable(Int8)"),

    # Top-bottom return (absolute value)
    ("top_bottom_return_abs_5", "Nullable(Float32)"),
    ("top_bottom_return_abs_10", "Nullable(Float32)"),
    ("top_bottom_return_abs_15", "Nullable(Float32)"),

    # Adjusted top return (equal weight)
    ("adj_top_return_abs_5_ew", "Nullable(Float32)"),
    ("adj_top_return_abs_10_ew", "Nullable(Float32)"),
    ("adj_top_return_abs_15_ew", "Nullable(Float32)"),

    # Adjusted top return (market cap weight)
    ("adj_top_return_abs_5_mw", "Nullable(Float32)"),
    ("adj_top_return_abs_10_mw", "Nullable(Float32)"),
    ("adj_top_return_abs_15_mw", "Nullable(Float32)"),

    # Sharpe ratio
    ("top_bottom_sr_5", "Nullable(Float32)"),
    ("top_bottom_sr_10", "Nullable(Float32)"),
    ("top_bottom_sr_15", "Nullable(Float32)"),

    # Distribution statistics
    ("factor_std", "Nullable(Float32)"),
    ("factor_skewness", "Nullable(Float32)"),
    ("factor_kurtosis", "Nullable(Float32)"),
    # Missing 1d rank_ic_ir
    ("rank_ic_ir", "Nullable(Float32)"),
    ("rank_ic_ir_5y", "Nullable(Float32)"),
    ("rank_ic_ir_1y", "Nullable(Float32)"),
]

# Add multi-horizon IC metrics
_HORIZONS = [2, 3, 5, 10, 20, 30]
_WINDOWS = ["", "_5y", "_1y"]
_METRICS = ["ic_mean", "rank_ic_mean", "ic_ir", "rank_ic_ir"]

for h in _HORIZONS:
    for w in _WINDOWS:
        for m in _METRICS:
            EVALUATION_METRICS.append((f"{m}_{h}d{w}", "Nullable(Float32)"))

# Schema for A2_factor_evaluate table
FACTOR_EVALUATE_SCHEMA = {
    "table_name": "A2_factor_evaluate",
    "columns": [
        ("factor_id", "String"),
        ("upgrade_datetime", "DateTime"),
    ] + EVALUATION_METRICS,
    "engine": "ReplacingMergeTree(upgrade_datetime)",
    "order_by": "factor_id",
    "primary_key": "factor_id",
}

# Schema for A3_factor_upgrade table
FACTOR_UPGRADE_SCHEMA = {
    "table_name": "A3_factor_upgrade",
    "columns": [
        ("factor_id", "String"),
        ("latest_date", "Nullable(Date)"),  # For daily factors
        ("latest_datetime", "Nullable(DateTime)"),  # For high-frequency factors
        ("upgrade_datetime", "DateTime"),
    ],
    "engine": "ReplacingMergeTree(upgrade_datetime)",
    "order_by": "factor_id",
    "primary_key": "factor_id",
}

# Schema for factor value tables (daily/low-frequency)
FACTOR_VALUE_SCHEMA = {
    "columns": [
        ("code", "String"),
        ("date", "Date"),
        ("factor_value", "Float32"),
    ],
    "engine": "ReplacingMergeTree()",
    "order_by": "(code, date)",
    "partition_by": "toYear(date)",
}

# Schema for high-frequency factor value tables
FACTOR_VALUE_HF_SCHEMA = {
    "columns": [
        ("code", "String"),
        ("date", "Date"),
        ("datetime", "DateTime('Asia/Shanghai')"),
        ("factor_value", "Float32"),
    ],
    "engine": "ReplacingMergeTree()",
    "order_by": "(code, datetime)",
    "partition_by": "toYYYYMM(date)",
}


def get_create_table_sql(schema: Dict, table_name: str = None, database: str = None) -> str:
    """
    Generate CREATE TABLE SQL from schema definition.

    Args:
        schema: Schema dictionary
        table_name: Override table name (optional)
        database: Database name (optional)

    Returns:
        CREATE TABLE SQL string
    """
    name = table_name or schema.get("table_name", "unnamed_table")
    if database:
        full_name = f"{database}.{name}"
    else:
        full_name = name

    columns_sql = ",\n    ".join(
        f"{col_name} {col_type}" for col_name, col_type in schema["columns"]
    )

    engine = schema.get("engine", "MergeTree()")
    order_by = schema.get("order_by", "tuple()")
    partition_by = schema.get("partition_by")

    sql = f"""CREATE TABLE IF NOT EXISTS {full_name}
(
    {columns_sql}
)
ENGINE = {engine}
ORDER BY ({order_by})"""

    if partition_by:
        sql += f"\nPARTITION BY {partition_by}"

    return sql


def get_evaluation_metric_columns() -> List[str]:
    """Get list of evaluation metric column names"""
    return [col_name for col_name, _ in EVALUATION_METRICS]


def get_all_table_schemas() -> Dict[str, Dict]:
    """Get all table schemas"""
    return {
        "basic": FACTOR_BASIC_SCHEMA,
        "evaluate": FACTOR_EVALUATE_SCHEMA,
        "upgrade": FACTOR_UPGRADE_SCHEMA,
    }


# =============================================================================
# Orthogonality Analysis Schemas
# =============================================================================

# Schema for B1_orthogonality_analysis (main summary table)
ORTHOGONALITY_ANALYSIS_SCHEMA = {
    "table_name": "B1_orthogonality_analysis",
    "columns": [
        # Primary key
        ("analysis_id", "String"),
        
        # Analysis scope
        ("asset_type", "String"),
        ("factor_type", "String"),
        ("analysis_datetime", "DateTime64(3)"),
        
        # Input info
        ("total_factors", "UInt32"),
        ("data_start_date", "Date"),
        ("data_end_date", "Date"),
        
        # Phase 1 summary
        ("effective_n_90", "UInt32"),
        ("effective_n_95", "UInt32"),
        ("mean_correlation", "Float32"),
        ("max_correlation", "Float32"),
        ("min_correlation", "Float32"),
        ("redundancy_ratio", "Float32"),
        ("high_corr_pairs_count", "UInt32"),
        
        # Phase 2 summary
        ("n_clusters", "UInt32"),
        ("linkage_method", "String"),
        ("n_central_factors", "UInt32"),
        ("n_peripheral_factors", "UInt32"),
        ("mst_total_distance", "Float32"),
        
        # Phase 3 summary
        ("n_selected", "UInt32"),
        ("n_removed", "UInt32"),
        ("selection_ratio", "Float32"),
        ("vif_threshold", "Float32"),
        ("marginal_ic_threshold", "Float32"),
        
        # File paths for large data
        ("correlation_matrix_path", "Nullable(String)"),
        ("distance_matrix_path", "Nullable(String)"),
        ("eigenvalues_path", "Nullable(String)"),
        ("linkage_matrix_path", "Nullable(String)"),
    ],
    "engine": "ReplacingMergeTree(analysis_datetime)",
    "order_by": "analysis_id",
    "partition_by": "toYYYYMM(analysis_datetime)",
}

# Schema for B2_high_correlation_pairs
HIGH_CORRELATION_PAIRS_SCHEMA = {
    "table_name": "B2_high_correlation_pairs",
    "columns": [
        ("analysis_id", "String"),
        ("factor_id_1", "String"),
        ("factor_id_2", "String"),
        ("correlation", "Float32"),
        ("distance", "Float32"),
        ("same_cluster", "UInt8"),
        ("cluster_id", "Nullable(UInt32)"),
    ],
    "engine": "MergeTree()",
    "order_by": "analysis_id, correlation",
}

# Schema for B3_factor_clusters
FACTOR_CLUSTERS_SCHEMA = {
    "table_name": "B3_factor_clusters",
    "columns": [
        ("analysis_id", "String"),
        ("factor_id", "String"),
        
        # Clustering info
        ("cluster_id", "UInt32"),
        ("cluster_size", "UInt32"),
        ("intra_cluster_corr", "Float32"),
        ("is_representative", "UInt8"),
        
        # MST info
        ("mst_degree", "UInt32"),
        ("centrality_score", "Float32"),
        ("is_central", "UInt8"),
        ("is_peripheral", "UInt8"),
        
        # Factor metrics (denormalized for query convenience)
        ("factor_ic", "Nullable(Float32)"),
        ("factor_rank_ic", "Nullable(Float32)"),
    ],
    "engine": "MergeTree()",
    "order_by": "analysis_id, cluster_id, factor_id",
}

# Schema for B4_mst_edges
MST_EDGES_SCHEMA = {
    "table_name": "B4_mst_edges",
    "columns": [
        ("analysis_id", "String"),
        ("edge_id", "UInt32"),
        ("source_factor", "String"),
        ("target_factor", "String"),
        ("distance", "Float32"),
        ("correlation", "Float32"),
        ("source_cluster", "UInt32"),
        ("target_cluster", "UInt32"),
        ("is_inter_cluster", "UInt8"),
    ],
    "engine": "MergeTree()",
    "order_by": "analysis_id, edge_id",
}

# Schema for B5_vif_results
VIF_RESULTS_SCHEMA = {
    "table_name": "B5_vif_results",
    "columns": [
        ("analysis_id", "String"),
        ("factor_id", "String"),
        ("cluster_id", "UInt32"),
        ("vif", "Float32"),
        ("r_squared", "Float32"),
        ("is_collinear", "UInt8"),
        ("recommended_action", "String"),
    ],
    "engine": "MergeTree()",
    "order_by": "analysis_id, cluster_id, vif",
}

# Schema for B6_factor_selection
FACTOR_SELECTION_SCHEMA = {
    "table_name": "B6_factor_selection",
    "columns": [
        ("analysis_id", "String"),
        ("factor_id", "String"),

        # Original IC
        ("original_ic", "Float32"),
        ("original_rank_ic", "Float32 DEFAULT 0"),

        # Marginal IC analysis
        ("marginal_ic", "Float32"),
        ("ic_contribution", "Float32"),
        ("is_significant", "UInt8"),

        # Selection result
        ("is_selected", "UInt8"),
        ("selection_order", "UInt32 DEFAULT 0"),  # 0 = not selected
        ("removal_reason", "String DEFAULT ''"),

        # Cluster info
        ("cluster_id", "UInt32"),
        ("is_cluster_rep", "UInt8"),
    ],
    "engine": "MergeTree()",
    "order_by": "analysis_id, is_selected, selection_order",
}

# Schema for B7_eigenvalue_distribution
EIGENVALUE_DISTRIBUTION_SCHEMA = {
    "table_name": "B7_eigenvalue_distribution",
    "columns": [
        ("analysis_id", "String"),
        ("component_index", "UInt32"),
        ("eigenvalue", "Float32"),
        ("explained_variance_ratio", "Float32"),
        ("cumulative_variance_ratio", "Float32"),
    ],
    "engine": "MergeTree()",
    "order_by": "analysis_id, component_index",
}


def get_orthogonality_schemas() -> Dict[str, Dict]:
    """Get all orthogonality analysis table schemas"""
    return {
        "analysis": ORTHOGONALITY_ANALYSIS_SCHEMA,
        "high_corr_pairs": HIGH_CORRELATION_PAIRS_SCHEMA,
        "clusters": FACTOR_CLUSTERS_SCHEMA,
        "mst_edges": MST_EDGES_SCHEMA,
        "vif_results": VIF_RESULTS_SCHEMA,
        "selection": FACTOR_SELECTION_SCHEMA,
        "eigenvalues": EIGENVALUE_DISTRIBUTION_SCHEMA,
    }
