import lime
import lime.lime_tabular
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import shap
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder


def prepare_data():
    """가상의 대출 심사 데이터를 생성하고 준비합니다."""
    print("--- 1. 데이터 준비 ---")
    data = {
        "나이": np.random.randint(20, 70, size=1000),
        "소득": np.random.randint(2000, 15000, size=1000),
        "부채": np.random.randint(0, 8000, size=1000),
        "신용등급": np.random.choice(
            ["A", "B", "C", "D"], size=1000, p=[0.3, 0.4, 0.2, 0.1]
        ),
        "대출기록": np.random.choice(["있음", "없음"], size=1000, p=[0.6, 0.4]),
    }
    df = pd.DataFrame(data)

    # Target 변수 생성 (규칙 기반)
    df["대출승인"] = (
        (df["소득"] > 5000)
        & (df["부채"] < df["소득"] * 0.4)
        & (df["신용등급"].isin(["A", "B"]))
    ).astype(int)

    # 범주형 변수 인코딩
    for col in ["신용등급", "대출기록"]:
        le = LabelEncoder()
        df[col] = le.fit_transform(df[col])

    X = df.drop("대출승인", axis=1)
    y = df["대출승인"]

    feature_names = list(X.columns)
    class_names = ["거절", "승인"]

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42
    )
    print("데이터 준비 완료.")
    return X_train, X_test, y_train, y_test, feature_names, class_names


def train_model(X_train, y_train):
    """분류 모델을 학습시킵니다."""
    print("\n--- 2. 모델 학습 ---")
    model = RandomForestClassifier(random_state=42)
    model.fit(X_train.values, y_train.values)
    print("RandomForestClassifier 모델 학습 완료.")
    return model


def analyze_with_lime(model, X_train, X_test, instance_idx, feature_names, class_names):
    """LIME을 사용하여 특정 예측 건을 분석하고 결과를 저장합니다."""
    print(f"\n--- 3. LIME 분석 (설명 대상: Test 데이터 {instance_idx}번) ---")
    explainer = lime.lime_tabular.LimeTabularExplainer(
        training_data=X_train.values,
        feature_names=feature_names,
        class_names=class_names,
        mode="classification",
    )

    instance = X_test.iloc[instance_idx].values
    explanation = explainer.explain_instance(
        instance, model.predict_proba, num_features=5
    )

    # LIME 결과 시각화 및 저장
    fig = explanation.as_pyplot_figure()
    lime_fig_path = "lime_report.png"
    fig.savefig(lime_fig_path, bbox_inches="tight")
    print(f"LIME 분석 결과를 '{lime_fig_path}' 파일로 저장했습니다.")
    plt.close()
    return explanation


def analyze_with_shap(model, X_train, X_test, instance_idx):
    """SHAP을 사용하여 로컬 및 글로벌 설명을 생성하고 결과를 저장합니다."""
    print("\n--- 4. SHAP 분석 ---")
    explainer = shap.TreeExplainer(model, X_train)
    shap_values = explainer.shap_values(X_test)

    # 로컬 설명 (Force Plot)
    print(f"SHAP 로컬 분석 (설명 대상: Test 데이터 {instance_idx}번)")
    instance = X_test.iloc[instance_idx]
    force_plot = shap.force_plot(
        explainer.expected_value[1],
        shap_values[1][instance_idx, :],
        instance,
        matplotlib=True,
        show=False,
    )
    shap_force_plot_path = "shap_force_plot.png"
    plt.savefig(shap_force_plot_path, bbox_inches="tight")
    print(f"SHAP Force Plot을 '{shap_force_plot_path}' 파일로 저장했습니다.")
    plt.close()

    # 글로벌 설명 (Summary Plot)
    print("SHAP 글로벌 분석 (전체 테스트 데이터)")
    shap.summary_plot(shap_values[1], X_test, show=False)
    shap_summary_plot_path = "shap_summary_plot.png"
    plt.savefig(shap_summary_plot_path, bbox_inches="tight")
    print(f"SHAP Summary Plot을 '{shap_summary_plot_path}' 파일로 저장했습니다.")
    plt.close()


def generate_report(model, X_test, instance_idx, lime_exp):
    """분석 결과를 종합하여 텍스트 보고서를 생성합니다."""
    print("\n--- 5. 최종 보고서 생성 ---")
    instance = X_test.iloc[instance_idx]
    prediction = model.predict(instance.values.reshape(1, -1))[0]
    pred_proba = model.predict_proba(instance.values.reshape(1, -1))[0]

    report = f"""
=================================================
  XAI 기반 대출 거절 사유 분석 보고서
=================================================

고객 정보:
{instance.to_string()}

-------------------------------------------------
모델 예측 결과:
- 예측: {'승인' if prediction == 1 else '거절'}
- 거절 확률: {pred_proba[0]:.2%}
- 승인 확률: {pred_proba[1]:.2%}
-------------------------------------------------

LIME 기반 주요 거절 요인 분석:
(이 피처들이 '거절' 예측에 기여했습니다)

"""
    # LIME 결과에서 거절에 영향을 미친 요인 추출
    for feature, weight in lime_exp.as_list(label=0):
        report += f"- {feature}\n"

    report += """
-------------------------------------------------
결론:
LIME 분석에 따르면, 위 목록의 피처들이 이번 대출 신청의 '거절' 결정에 주요하게 작용한 것으로 보입니다.
SHAP 분석 결과(첨부 이미지 참조) 또한 이러한 경향을 뒷받침합니다.

첨부 파일:
1. lime_report.png (LIME 시각화)
2. shap_force_plot.png (SHAP 개별 예측 분석)
3. shap_summary_plot.png (SHAP 모델 전체 분석)
=================================================
"""
    report_path = "xai_analysis_report.txt"
    with open(report_path, "w", encoding="utf-8") as f:
        f.write(report)
    print(f"최종 분석 보고서를 '{report_path}' 파일로 저장했습니다.")


def main():
    """XAI 분석 및 보고서 생성 파이프라인을 실행합니다."""
    X_train, X_test, y_train, y_test, feature_names, class_names = prepare_data()
    model = train_model(X_train, y_train)

    # 분석할 인스턴스 선택 (예: 첫 번째 거절 건)
    refused_indices = y_test[y_test == 0].index
    if len(refused_indices) > 0:
        instance_to_explain_idx = refused_indices[0]
        instance_to_explain_loc = X_test.index.get_loc(instance_to_explain_idx)
    else:
        print("테스트 데이터에 거절 건이 없어 임의의 데이터를 사용합니다.")
        instance_to_explain_loc = 0

    lime_explanation = analyze_with_lime(
        model, X_train, X_test, instance_to_explain_loc, feature_names, class_names
    )
    analyze_with_shap(model, X_train, X_test, instance_to_explain_loc)
    generate_report(model, X_test, instance_to_explain_loc, lime_explanation)

    print("\n모든 실습 과정이 완료되었습니다.")


if __name__ == "__main__":
    main()
