#!/usr/bin/env python
import warnings
warnings.filterwarnings('ignore')
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import roc_auc_score
from catboost import CatBoostClassifier
N_SPLITS = 5
SEED = 42
RARE_THR = 20
MAX_EARLY_STOP = 200
CTB_ROUNDS = 1000
TRAIN_PATH = r"data/train.csv"
TEST_PATH = r"data/testB.csv"
SUB_PATH = r"data/1127/submitB_ctb.csv"
def load_data(train_path, test_path):
    return pd.read_csv(train_path), pd.read_csv(test_path)
def rare_merge(series, thr=RARE_THR):
    vc = series.value_counts()
    rare = vc[vc < thr].index
    return series.where(~series.isin(rare), "__RARE__")
def feature_engineering(X, T, y):
    region_counts = pd.concat([X['region'], T['region']]).value_counts() # 自动特征
    X['region_vcounts'] = X['region'].map(region_counts)
    T['region_vcounts'] = T['region'].map(region_counts)
    # 基础预处理
    for df in [X, T]:
        df['daysaddx19'] = df['days'] + df['x19'] #自动特征
        df["month_idx"] = df["month"].apply(lambda x: int(str(x)[1:]) if pd.notna(x) else -1)
        source_parts = df['source'].str.split('|', expand=True)
        df['CAR_type'] = source_parts[0].str.replace('CAR_', '').astype(int)
        df['ENG_type'] = source_parts[1].str.replace('ENG_', '').astype(int)
        df["t3_numeric"] = df["t3"].apply(lambda x: float(str(x)[:-1]) if pd.notna(x) else np.nan)
        df["t3_unit"] = df["t3"].apply(lambda x: str(x)[-1] if pd.notna(x) else "NONE")
        order_map = {'s': 1, 'ss': 2, 'sss': 3}
        df['grades'] = df['grades'].map(order_map).fillna(0).astype(int)
        df['log_days'] = np.log1p(df['days'])
        df['condition'].fillna(0, inplace=True)
        # 数值交互特征
        df['age_cc_inter'] = df['age_range'] * df['cc']
        df['cc_x_V'] = df['cc'] * df['V']
        df['cond_per_age'] = df['condition'].abs() / (1.0 + df['age_range'])
        # 类别交叉特征
        df['region_age_cat'] = df['region'].astype(str) + '_' + df['age_range'].astype(str)
        df['region_month'] = df['region'].astype(str) + '|' + df['month_idx'].astype(str)
        # condition相关特征
        df['condition_abs_age_ratio'] = df['condition'].abs() / (df['log_days'] / 365.25 + 1e-5)
        df['cond_resid_region'] = df['condition'] - df.groupby('region')['condition'].transform('median')
        df['cc_x_V_condition_ratio'] = df['cc'] * df['V'] / (df['condition'] + 1e-6)
    # 类别特征编码
    cat_cols = [c for c in ['source','month', 'region', 'code', 't3_unit', 'region_age_cat', 'region_month'] if c in X.columns]
    for c in cat_cols:
        X[c] = rare_merge(X[c].astype(str), RARE_THR)
        T[c] = T[c].astype(str)
        known = set(np.concatenate([X[c].unique(), T[c].unique()]))
        T[c] = T[c].where(T[c].isin(known), "__UNK__")
        le = LabelEncoder()
        all_vals = list(set(X[c].unique()) | set(T[c].unique()))
        le.fit(all_vals)
        X[c] = le.transform(X[c])
        T[c] = le.transform(T[c])
    # 特征选择
    exclude_cols = ["id", "label", "t1", "t2", "t3",  "x1", "x4", "x6", "x7", "x9", "x11", "x12", "x13", "x14",
                    "x15", "x17", "x18", "x19", "x20",  "ratio",  "days",'version' ]
    features = [c for c in X.columns if c not in exclude_cols and X[c].dtype in [np.int64, np.float64,np.int32]]
    # 缺失值处理
    X = X[features].replace([np.inf, -np.inf], np.nan).fillna(-999)
    T = T[features].replace([np.inf, -np.inf], np.nan).fillna(-999)
    return X, T, features, cat_cols
if __name__ == "__main__":
    train, test = load_data(TRAIN_PATH, TEST_PATH)
    y_train = train["label"].copy()
    # 特征工程
    X_processed, T_processed, features, cat_cols = feature_engineering(train, test, y_train)
    # CatBoost模型训练
    skf = StratifiedKFold(n_splits=N_SPLITS, shuffle=True, random_state=SEED)
    oof_ctb = np.zeros(len(X_processed))
    pred_ctb = np.zeros(len(T_processed))
    cat_idx = [features.index(c) for c in cat_cols if c in features]
    for fold, (tr_idx, val_idx) in enumerate(skf.split(X_processed, y_train), 1):
        print(f'Fold {fold} started')
        X_tr, X_va = X_processed.iloc[tr_idx][features], X_processed.iloc[val_idx][features]
        y_tr, y_va = y_train.iloc[tr_idx], y_train.iloc[val_idx]
        ctb_params = {
            'loss_function': 'Logloss', 'eval_metric': 'AUC', 'learning_rate': 0.03,
            'depth': 7, 'l2_leaf_reg': 6, 'iterations': CTB_ROUNDS, 'random_seed': SEED + fold, 'verbose': False        }
        ctb_model = CatBoostClassifier(**ctb_params)
        ctb_model.fit(X_tr, y_tr, eval_set=(X_va, y_va), cat_features=cat_idx,
                      early_stopping_rounds=MAX_EARLY_STOP, verbose=False)
        oof_ctb[val_idx] = ctb_model.predict_proba(X_va)[:, 1]
        pred_ctb += ctb_model.predict_proba(T_processed[features])[:, 1] / N_SPLITS
        print(f'Fold {fold}: CTB AUC = {roc_auc_score(y_va, oof_ctb[val_idx]):.5f}')
    print(f'OOF CTB AUC: {roc_auc_score(y_train, oof_ctb):.5f}')
    # 生成提交文件
    submission = pd.read_csv('data/submit_exampleB.csv')
    submission['label'] = pred_ctb
    submission.to_csv(SUB_PATH, index=False)
    print(f'Submission saved to {SUB_PATH}')

## 之前0.690跑
# fold x 1 17062 4266 17062 4266
# [Fold 1] LGB AUC=0.70543, XGB AUC=0.70964, CTB AUC=0.70521, RF AUC=0.70700
# fold x 2 17062 4266 17062 4266
# [Fold 2] LGB AUC=0.69733, XGB AUC=0.69995, CTB AUC=0.70489, RF AUC=0.70061
# fold x 3 17062 4266 17062 4266
# [Fold 3] LGB AUC=0.68470, XGB AUC=0.68607, CTB AUC=0.68681, RF AUC=0.69136
# fold x 4 17063 4265 17063 4265
# [Fold 4] LGB AUC=0.67845, XGB AUC=0.68764, CTB AUC=0.68833, RF AUC=0.68872
# fold x 5 17063 4265 17063 4265
# [Fold 5] LGB AUC=0.69001, XGB AUC=0.69002, CTB AUC=0.69670, RF AUC=0.69728
# [OOF] LGB AUC = 0.686804
# [OOF] XGB AUC = 0.693587
# [OOF] CTB AUC = 0.695874
# [OOF] RF AUC = 0.696963
#
# [Feature Importance - Top 20]
#                  feature    importance
# 57        region_age_cat  37996.562543
# 163  region_new_type_x19  34578.507581
# 31      daysdivcondition  22620.808301
# 104     region_age_range  13831.724061
# [OOF] META-LR AUC = 0.706936
# [OOF] Rank-Average AUC = 0.702005

## baseline跑 提交分数：0.676
# ['id', 't3', 'version']
# Fold 1 started
# Fold 1: CTB AUC = 0.69665
# Fold 2 started
# Fold 2: CTB AUC = 0.68462
# Fold 3 started
# Fold 3: CTB AUC = 0.67217
# Fold 4 started
# Fold 4: CTB AUC = 0.68313
# Fold 5 started
# Fold 5: CTB AUC = 0.67738
# OOF CTB AUC: 0.68229
# Submission saved to data/submitB_ctb.csv