"""
07_pca_dual_track_weighting.py
双轨权重模块：主成分分析（PCA）经验权重 + 规范性（Delphi 式等权/专家）权重融合
Dual-Track Weighting Module: PCA empirical weights blended with normative weights

用途 / Purpose:
    对三大维度（V 财政响应力 / C 转化能力 / F 制度摩擦）各自内部的标准化指标
    （06 输出的 panel_normalized_minmax.csv）分别做主成分分析，取第一主成分
    (PC1) 的载荷绝对值归一化后作为"经验权重"；再与预设的规范性等权方案按
    ALPHA_BLEND 比例融合，得到最终的指标层权重。

    经验校准目标（参见 oecd38_WORKLOG_FINAL.md §3 阶段二）：
        V 维度 PC1 方差解释率 ≈ 36.0%
        C 维度 PC1 方差解释率 ≈ 53.5%
        F 维度 PC1 方差解释率 ≈ 53.7%

依赖 / Dependencies: pandas, numpy, scikit-learn
随机种子 / Random seed: 42
"""

import json
import logging
from pathlib import Path

import numpy as np
import pandas as pd
from sklearn.decomposition import PCA

logging.basicConfig(level=logging.INFO, format="%(asctime)s  %(message)s")
log = logging.getLogger("07_pca_weighting")

DATA_DIR = Path("./data")
RANDOM_SEED = 42
ALPHA_BLEND = 0.6  # 0 = 纯规范性等权, 1 = 纯 PCA 经验权重

# 三大维度所含指标（对应 06 输出列名，均以 __minmax 结尾）
DIMENSIONS = {
    "V": [  # 财政响应力 Fiscal Responsiveness
        "gov_expenditure_pct_gdp__minmax",
        "social_protection_expenditure__minmax",
        "fiscal_balance_pct_gdp__minmax",
        "public_debt_pct_gdp__minmax",
        "tax_revenue_pct_gdp__minmax",
        "unemployment_benefit_coverage__minmax",
    ],
    "C": [  # 转化能力 Conversion Capacity
        "gdp_growth_pct__minmax",
        "gross_capital_formation_pct_gdp__minmax",
        "patents_residents__minmax",
        "rd_expenditure_pct_gdp__minmax",
        "labor_productivity_growth__minmax",
        "high_tech_exports_pct__minmax",
    ],
    "F": [  # 制度摩擦 Institutional Friction (反向指标，越高摩擦越大)
        "regulatory_quality__minmax",
        "government_effectiveness__minmax",
        "rule_of_law__minmax",
        "corruption_control__minmax",
        "bureaucracy_delay_index__minmax",
    ],
}

# 规范性（等权）方案，作为经验权重的锚定基准
NORMATIVE_WEIGHTS = {
    dim: {col: 1.0 / len(cols) for col in cols} for dim, cols in DIMENSIONS.items()
}


def pca_first_component_weights(df: pd.DataFrame, cols: list) -> tuple:
    """对给定维度的指标子集做 PCA，返回 (归一化后的 PC1 载荷权重字典, 方差解释率)."""
    sub = df[cols].dropna()
    pca = PCA(n_components=1, random_state=RANDOM_SEED)
    pca.fit(sub.to_numpy())
    loadings = np.abs(pca.components_[0])
    weights = loadings / loadings.sum()
    explained = float(pca.explained_variance_ratio_[0])
    return dict(zip(cols, weights)), explained


def blend_weights(pca_w: dict, normative_w: dict, alpha: float = ALPHA_BLEND) -> dict:
    """按 alpha 比例融合经验权重与规范性权重，并重新归一化到和为 1。"""
    blended = {k: alpha * pca_w[k] + (1 - alpha) * normative_w[k] for k in pca_w}
    total = sum(blended.values())
    return {k: v / total for k, v in blended.items()}


def main():
    panel = pd.read_csv(DATA_DIR / "panel_normalized_minmax.csv")

    dimension_weights = {}
    variance_report = {}

    for dim, cols in DIMENSIONS.items():
        available_cols = [c for c in cols if c in panel.columns]
        pca_w, explained = pca_first_component_weights(panel, available_cols)
        blended = blend_weights(pca_w, {k: NORMATIVE_WEIGHTS[dim][k] for k in available_cols})
        dimension_weights[dim] = blended
        variance_report[dim] = explained
        log.info(
            "Dimension %s: PC1 explained variance = %.1f%% (target reference from WORKLOG_FINAL)",
            dim, explained * 100,
        )

    output = {
        "alpha_blend": ALPHA_BLEND,
        "random_seed": RANDOM_SEED,
        "variance_explained": variance_report,
        "weights": dimension_weights,
    }
    with open(DATA_DIR / "dimension_weights.json", "w", encoding="utf-8") as f:
        json.dump(output, f, ensure_ascii=False, indent=2)

    log.info("Dual-track dimension weights saved -> data/dimension_weights.json")


if __name__ == "__main__":
    main()
