# -*- coding: utf-8 -*-
"""
大外しサンプル抽出スクリプト。
predictions.csv と features.csv を sample_id で結合し、
target ごとに abs(error) 上位50件を原因分類用CSVとして出力する。
"""
import os
import pandas as pd

BASE = r"C:/Users/NX023286.NEXYZ/Documents/開発しているもの/制作中/AIラーメン評価アルゴリズム分析"
DSC = os.path.join(BASE, "検証", "results", "downstream_model_comparison")
OUT_DIR = os.path.join(BASE, "精度改善_20260619", "01_error_analysis")
os.makedirs(OUT_DIR, exist_ok=True)

TOP_N = 50

pred = pd.read_csv(os.path.join(DSC, "predictions.csv"), encoding="utf-8-sig")
feat = pd.read_csv(os.path.join(DSC, "features.csv"), encoding="utf-8-sig")

# モデル名（日本語）
LINEAR = "線形回帰"
RF = "Random Forest"
LGBM = "LightGBM"

# features 側で使う列
FEAT_COLS = [
    "sample_id", "video_path", "frame_path", "store", "slot", "brix",
    "visual_density", "visual_density_num", "water_level", "oil_emulsification",
    "boiling_heat_state", "photo_quality", "image_condition_score",
    "assessable", "retake_recommended", "density_brix_abs_diff",
]
feat_sub = feat[FEAT_COLS].copy()


def pick_split(df):
    """test を優先。test が無ければ test+val。"""
    t = df[df["split"] == "test"]
    if len(t) > 0:
        return t
    return df[df["split"].isin(["test", "val"])]


def get_subset(target, model, feature_set):
    s = pred[(pred["target"] == target)
             & (pred["model"] == model)
             & (pred["feature_set"] == feature_set)]
    s = pick_split(s)
    return s[["sample_id", "human_score", "prediction", "error"]].copy()


def build(target, main_model, main_fs, compare_specs, out_name):
    """compare_specs: list of (label, model, feature_set)"""
    main = get_subset(target, main_model, main_fs)
    main = main.rename(columns={
        "prediction": "main_prediction",
        "error": "main_error",
    })
    main["abs_error"] = main["main_error"].abs()

    # 比較モデルを横に結合
    df = main.copy()
    for label, m, fs in compare_specs:
        comp = get_subset(target, m, fs)[["sample_id", "prediction", "error"]]
        comp = comp.rename(columns={
            "prediction": f"{label}_prediction",
            "error": f"{label}_error",
        })
        df = df.merge(comp, on="sample_id", how="left")

    # features 結合
    df = df.merge(feat_sub, on="sample_id", how="left")

    # 上位50件
    df = df.sort_values("abs_error", ascending=False).head(TOP_N).reset_index(drop=True)

    # summary 列（人手確認の手がかり）
    def make_summary(r):
        return (f"{r['store']}/{r['slot']} "
                f"教師{r['human_score']:.0f} vs 予測{r['main_prediction']:.1f} "
                f"(誤差{r['main_error']:+.1f}) "
                f"brix={r['brix']} 濃度={r['visual_density']} "
                f"画質={r['photo_quality']} 判定可={r['assessable']}")
    df["summary"] = df.apply(make_summary, axis=1)

    # 人手分類用の空列
    df["分類"] = ""  # 候補: フレーム抽出ミス/LLM評価ミス/濃度ズレ/教師点数ブレ/画像では判断不能/その他
    df["メモ"] = ""

    # 列順
    compare_cols = []
    for label, _, _ in compare_specs:
        compare_cols += [f"{label}_prediction", f"{label}_error"]

    cols = (["sample_id", "video_path", "frame_path", "store", "slot",
             "human_score", "main_prediction", "main_error", "abs_error"]
            + compare_cols
            + ["brix", "visual_density", "visual_density_num", "water_level",
               "oil_emulsification", "boiling_heat_state", "photo_quality",
               "image_condition_score", "assessable", "retake_recommended",
               "density_brix_abs_diff", "summary", "分類", "メモ"])
    df = df[cols]

    out_path = os.path.join(OUT_DIR, out_name)
    df.to_csv(out_path, index=False, encoding="utf-8-sig")
    return df, out_path


def report(name, df, path):
    print(f"\n=== {name} ===")
    print(f"出力: {path}")
    print(f"行数: {len(df)}")
    print(f"abs_error 最小: {df['abs_error'].min():.2f}  最大: {df['abs_error'].max():.2f}")
    print("上位5件 (sample_id, abs_error):")
    for _, r in df.head(5).iterrows():
        print(f"  sample_id={int(r['sample_id'])}  abs_error={r['abs_error']:.2f}")


# human_total_score: 主軸=線形回帰 visual_plus_brix, 比較=RF/LGBM (visual_plus_brix_slot)
tot_df, tot_path = build(
    target="human_total_score",
    main_model=LINEAR, main_fs="visual_plus_brix",
    compare_specs=[("rf", RF, "visual_plus_brix_slot"),
                   ("lgbm", LGBM, "visual_plus_brix_slot")],
    out_name="large_errors_human_total.csv",
)

# human_soup_score: 主軸=線形回帰 visual_only
soup_df, soup_path = build(
    target="human_soup_score",
    main_model=LINEAR, main_fs="visual_only",
    compare_specs=[("rf", RF, "visual_only"),
                   ("lgbm", LGBM, "visual_only")],
    out_name="large_errors_human_soup.csv",
)

report("human_total_score", tot_df, tot_path)
report("human_soup_score", soup_df, soup_path)
