"""Diagramme de longévité des personnages (bornes d'apparition)."""

from pathlib import Path
from typing import Dict, List

import matplotlib.pyplot as plt
from matplotlib.patches import Patch

from lib.filesystem import ensure_parent_dir
from lib.milestones import load_milestones
from lib.plots.gender_palette import GENDER_COLORS, GENDER_LABELS
from lib.rebrickable.stats import read_rows


def load_spans(path: Path) -> List[dict]:
    """Charge le CSV des bornes min/max par personnage."""
    return read_rows(path)


def plot_character_spans(spans_path: Path, destination_path: Path, milestones_path: Path | None = None) -> None:
    """Trace un diagramme en barres représentant la longévité des personnages."""
    rows = load_spans(spans_path)
    if not rows:
        return
    characters = [row["known_character"] for row in rows]
    starts = [int(row["start_year"]) for row in rows]
    ends = [int(row["end_year"]) for row in rows]
    counts = [int(row["total_minifigs"]) for row in rows]
    genders = [row.get("gender", "") for row in rows]
    colors = [GENDER_COLORS.get(gender.strip().lower(), GENDER_COLORS["unknown"]) for gender in genders]
    positions = list(range(len(rows)))
    widths = [end - start + 1 for start, end in zip(starts, ends)]
    min_year = min(starts)
    max_year = max(ends)
    height = max(5, len(rows) * 0.3)

    milestones = load_milestones(milestones_path) if milestones_path else []

    fig, ax = plt.subplots(figsize=(12, height))
    bars = ax.barh(
        positions,
        widths,
        left=starts,
        color=colors,
        edgecolor="#0d0d0d",
        linewidth=0.6,
    )
    ax.set_yticks(positions)
    ax.set_yticklabels(characters)
    ax.set_xlabel("Années d'apparition")
    ax.set_ylabel("Personnage")
    ax.set_title("Longévité des personnages (première à dernière apparition)")
    ax.set_xlim(min_year - 1, max_year + 1)
    ax.grid(True, axis="x", linestyle="--", alpha=0.25)
    for bar, start, end, count in zip(bars, starts, ends, counts):
        label = f"{start}–{end} ({count})" if start != end else f"{start} ({count})"
        ax.text(
            start + (end - start) / 2,
            bar.get_y() + bar.get_height() / 2,
            label,
            ha="center",
            va="center",
            fontsize=8,
            color="#0d0d0d",
        )

    legend_entries = []
    seen = set()
    for gender in genders:
        normalized = gender.strip().lower()
        if normalized in seen:
            continue
        seen.add(normalized)
        legend_entries.append(
            Patch(
                facecolor=GENDER_COLORS.get(normalized, GENDER_COLORS["unknown"]),
                edgecolor="#0d0d0d",
                linewidth=0.6,
                label=GENDER_LABELS.get(normalized, "Inconnu"),
            )
        )
    if legend_entries:
        ax.legend(handles=legend_entries, title="Genre", loc="lower right")

    if milestones:
        milestones_in_range = sorted(
            [m for m in milestones if min_year <= m["year"] <= max_year],
            key=lambda m: (m["year"], m["description"]),
        )
        milestone_offsets: Dict[int, int] = {}
        offset_step = 0.2
        max_offset = 0
        y_bottom, y_top = ax.get_ylim()
        text_y = y_top - (y_top - y_bottom) * 0.01
        for milestone in milestones_in_range:
            year = milestone["year"]
            count_for_year = milestone_offsets.get(year, 0)
            milestone_offsets[year] = count_for_year + 1
            horizontal_offset = offset_step * (count_for_year // 2 + 1)
            max_offset = max(max_offset, count_for_year)
            if count_for_year % 2 == 1:
                horizontal_offset *= -1
            text_x = year + horizontal_offset
            ax.axvline(year, color="#d62728", linestyle="--", linewidth=1, alpha=0.65)
            ax.text(
                text_x,
                text_y,
                milestone["description"],
                rotation=90,
                verticalalignment="top",
                horizontalalignment="center",
                fontsize=8,
                color="#d62728",
            )

    ensure_parent_dir(destination_path)
    fig.tight_layout()
    fig.savefig(destination_path, dpi=160)
    plt.close(fig)
