#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Final version with external top legend and adaptive mean annotations inside each box.
LOCI detector is fully excluded from all analysis (filtered before rank computation).
"""

import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.patches import Patch
import logging

# only show ERROR+ messages from the text module
logging.getLogger("matplotlib.text").setLevel(logging.ERROR)

# -----------------------------
# Load & Preprocess CSVs
# -----------------------------
base_path="./final_metrics/all/"
file_template = "Experiments_random_seed_{}.csv"
seeds = [0, 1, 2, 3, 4]
df_list = []

for seed in seeds:
    file_path = os.path.join(base_path, file_template.format(seed))
    if os.path.exists(file_path):
        df = pd.read_csv(file_path)
        df.rename(columns={df.columns[0]: "Dataset", df.columns[1]: "Detector"}, inplace=True)

        # Convert relevant columns to numeric safely
        df["PR"] = pd.to_numeric(df["PR"], errors="coerce")
        df["AUC"] = pd.to_numeric(df["AUC"], errors="coerce")

        # ❗ Remove LOCI and FlowMatchingTrajectory before computing ranks to avoid influencing other detectors
        df = df[df["Detector"] != "LOCI"]
        df = df[df["Detector"] != "FlowMatchingTrajectory"]

        df["Seed"] = seed

        # Compute per-seed, per-dataset ranking (lower rank = better)
        df["Rank_PR"] = df.groupby(["Seed", "Dataset"])["PR"].rank(ascending=False, method="min")
        df["Rank_AUC"] = df.groupby(["Seed", "Dataset"])["AUC"].rank(ascending=False, method="min")

        df_list.append(df)
        print(f"Processed Seed {seed}")
    else:
        print(f"File missing for Seed {seed}: {file_path}")

# -----------------------------
# Aggregate Metrics across seeds
# -----------------------------
if df_list:
    df_combined = pd.concat(df_list, ignore_index=True)

    # Compute mean/std of metrics for each Detector x Dataset combo
    df_aggregated = df_combined.groupby(["Dataset", "Detector"]).agg(
        Mean_PR=("PR", "mean"),
        Mean_AUC=("AUC", "mean"),
        Std_PR=("PR", "std"),
        Std_AUC=("AUC", "std"),
        Mean_ExecTime=("~ExecTimeSeconds", "mean"),
        Std_ExecTime=("~ExecTimeSeconds", "std"),
        Mean_TrainTime=("~TrainTimeSeconds", "mean"),
        Std_TrainTime=("~TrainTimeSeconds", "std"),
        Mean_TestTime=("~TestTimeSeconds", "mean"),
        Std_TestTime=("~TestTimeSeconds", "std"),
    ).reset_index()

    # Round numeric values for cleaner display
    df_aggregated[df_aggregated.select_dtypes(include=[np.number]).columns] = \
        df_aggregated.select_dtypes(include=[np.number]).round(3)

    # Rank detectors by Mean PR and AUC within each dataset
    df_aggregated["Rank_PR"] = df_aggregated.groupby("Dataset")["Mean_PR"].rank(ascending=False, method="min")
    df_aggregated["Rank_AUC"] = df_aggregated.groupby("Dataset")["Mean_AUC"].rank(ascending=False, method="min")

    # Rename AUC → ROC to avoid confusion in plots
    df_aggregated.rename(columns={
        "Mean_AUC": "Mean_ROC",
        "Std_AUC": "Std_ROC",
        "Rank_AUC": "Rank_ROC"
    }, inplace=True)

# -----------------------------
# Define Detector Categories & Colors
# -----------------------------
# Note: LOCI is fully removed
transductive_detectors = ["ABOD", "COF", "LOF", "PCA", "KPCA", "KNN", "INNE"]
inductive_detectors = [
    "CBLOF", "IForest", "LODA", "FeatureBagging", "Sampling", "MCD", "CD",
    "ECOD", "HBOS", "OCSVM", "KDE", "GMM", "QMCD", "LMDD"
]

def assign_category(detector):
    if detector in transductive_detectors:
        return "Transductive"
    elif detector in inductive_detectors:
        return "Inductive"
    else:
        return "Deep Learning"

df_aggregated["Category"] = df_aggregated["Detector"].apply(assign_category)

category_colors = {
    "Transductive": (102/255, 194/255, 165/255, 0.75),
    "Inductive": (141/255, 160/255, 203/255, 0.75),
    "Deep Learning": (252/255, 141/255, 98/255, 0.75)
}

detector_palette = {
    detector: category_colors[assign_category(detector)]
    for detector in df_aggregated["Detector"].unique()
}

legend_elements = [
    Patch(facecolor=category_colors["Deep Learning"], edgecolor='black', label='Deep Learning'),
    Patch(facecolor=category_colors["Transductive"], edgecolor='black', label='Transductive'),
    Patch(facecolor=category_colors["Inductive"], edgecolor='black', label='Inductive'),
]

# -----------------------------
# Helper Function: Annotate Mean Ranks
# -----------------------------
def annotate_means(ax, data, order, value_column):
    for i, detector in enumerate(order):
        mean_val = data[data["Detector"] == detector][value_column].mean()
        font_size = max(6, 12 - len(order) // 6)  # Scale text for readability
        ax.text(i, mean_val, f"{mean_val:.1f}", color='blue', ha='center', va='center', fontweight='bold', fontsize=font_size)

# -----------------------------
# Plot: Rank_ROC with annotated means
# -----------------------------
order_roc = df_aggregated.groupby("Detector")["Rank_ROC"].mean().sort_values().index.tolist()
fig, ax = plt.subplots(figsize=(12, 8))
sns.boxplot(x="Detector", y="Rank_ROC", data=df_aggregated, order=order_roc, palette=detector_palette, ax=ax, hue="Detector", legend=False)
plt.title("Distribution of Rank_ROC by Detector")
plt.xlabel("Detector")
plt.ylabel("Rank_ROC")
plt.xticks(rotation=45, ha='right')
annotate_means(ax, df_aggregated, order_roc, "Rank_ROC")
plt.legend(
    handles=legend_elements,
    title="Detector Type",
    loc="upper center",
    bbox_to_anchor=(0.5, 1.25),
    ncol=3,
    frameon=True,
    fontsize=9,
    title_fontsize=10
)
plt.tight_layout(rect=[0, 0, 1, 0.9])
plt.savefig("Rank_ROC.pdf", bbox_inches='tight')
plt.show()

# -----------------------------
# Plot: Rank_PR with annotated means
# -----------------------------
order_pr = df_aggregated.groupby("Detector")["Rank_PR"].mean().sort_values().index.tolist()
fig, ax = plt.subplots(figsize=(12, 8))
sns.boxplot(x="Detector", y="Rank_PR", data=df_aggregated, order=order_pr, palette=detector_palette, ax=ax, hue="Detector", legend=False)
plt.title("Distribution of Rank_PR by Detector")
plt.xlabel("Detector")
plt.ylabel("Rank_PR")
plt.xticks(rotation=45, ha='right')
annotate_means(ax, df_aggregated, order_pr, "Rank_PR")
plt.legend(
    handles=legend_elements,
    title="Detector Type",
    loc="upper center",
    bbox_to_anchor=(0.5, 1.25),
    ncol=3,
    frameon=True,
    fontsize=9,
    title_fontsize=10
)
plt.tight_layout(rect=[0, 0, 1, 0.9])
plt.savefig("Rank_PR.pdf", bbox_inches='tight')
plt.show()