#!/usr/bin/env python3
"""
hu-agent adoption visualisation — 3 charts + 3 tables.

Denominator throughout: (hu-agent + human) PRs. Other bots
(github-actions, humand-react-bot, etc.) are excluded — they remain in the
`prsMergedOtherBots` column for reference.

Inputs   analytics/data/weekly-metrics.csv      → global weekly adoption
         analytics/data/repo-adoption.csv       → per-repo rollup (window total)
         analytics/data/squad-adoption.csv      → per-squad rollup (window total)
Outputs  analytics/charts/01-global-adoption.png
         analytics/charts/02-adoption-by-repo.png
         analytics/charts/03-adoption-by-squad.png
"""

from __future__ import annotations

import sys
from pathlib import Path

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import pandas as pd

DATA_DIR = Path(__file__).parent / "data"
OUT_DIR = Path(__file__).parent / "charts"
WEEKLY_CSV = DATA_DIR / "weekly-metrics.csv"
REPO_CSV = DATA_DIR / "repo-adoption.csv"
SQUAD_CSV = DATA_DIR / "squad-adoption.csv"

BOT_COLOR = "#f59e0b"       # amber — hu-agent
HUMANS_COLOR = "#94a3b8"    # slate — human engineers
LINE_COLOR = "#ef4444"      # red — adoption curve


def ensure(path: Path) -> None:
    if not path.exists():
        sys.stderr.write(
            f"Missing {path}. Run `bun run analytics:scrape` first.\n",
        )
        sys.exit(1)


def compute_adoption(df: pd.DataFrame) -> pd.DataFrame:
    """Adds prsMergedHumans + engTotal + share columns (share = bot/engTotal%)."""
    for col in ("prsMergedTotal", "prsMergedBot", "prsMergedOtherBots"):
        df[col] = pd.to_numeric(df[col], errors="coerce").fillna(0).astype(int)
    df["prsMergedHumans"] = (
        df["prsMergedTotal"] - df["prsMergedBot"] - df["prsMergedOtherBots"]
    ).clip(lower=0)
    df["engTotal"] = df["prsMergedBot"] + df["prsMergedHumans"]
    df["share"] = (df["prsMergedBot"] / df["engTotal"].replace(0, pd.NA) * 100).fillna(0)
    return df


# ── Chart 1: global weekly adoption curve ─────────────────────────────

def chart_global(df: pd.DataFrame) -> None:
    fig, ax_bars = plt.subplots(figsize=(13, 6))
    x_pos = list(range(len(df)))
    x_labels = df["weekStart"].dt.strftime("%Y-%m-%d")

    ax_bars.bar(x_pos, df["prsMergedHumans"], color=HUMANS_COLOR, label="PRs humanos", alpha=0.6)
    ax_bars.bar(
        x_pos,
        df["prsMergedBot"],
        bottom=df["prsMergedHumans"],
        color=BOT_COLOR,
        label="PRs hu-agent",
        alpha=0.9,
    )
    ax_bars.set_ylabel("PRs mergeados (humanos + hu-agent)")
    ax_bars.set_xlabel("Semana (lunes)")
    ax_bars.set_xticks(x_pos)
    ax_bars.set_xticklabels(x_labels, rotation=30, ha="right")
    ax_bars.yaxis.set_major_locator(ticker.MaxNLocator(integer=True))
    ax_bars.grid(True, axis="y", alpha=0.2)

    ax_line = ax_bars.twinx()
    ax_line.plot(
        x_pos, df["share"], color=LINE_COLOR, marker="o",
        linewidth=2.2, markersize=7, label="Adopción (%)",
    )
    for i, v in enumerate(df["share"]):
        ax_line.text(i, v + 0.25, f"{v:.1f}%", ha="center", va="bottom",
                     fontsize=9, color=LINE_COLOR, fontweight="bold")
    ax_line.set_ylabel("Adopción hu-agent (%)", color=LINE_COLOR)
    ax_line.tick_params(axis="y", labelcolor=LINE_COLOR)
    ax_line.set_ylim(bottom=0, top=max(df["share"].max() * 1.25, 1.0))
    ax_line.yaxis.set_major_formatter(ticker.PercentFormatter(decimals=1))

    ax_bars.set_title("Curva de adopción de hu-agent — global (semanal)")

    h1, l1 = ax_bars.get_legend_handles_labels()
    h2, l2 = ax_line.get_legend_handles_labels()
    ax_bars.legend(h1 + h2, l1 + l2, loc="upper left")

    fig.tight_layout()
    out = OUT_DIR / "01-global-adoption.png"
    fig.savefig(out, dpi=150)
    plt.close(fig)
    print(f"wrote {out}")


# ── Chart 2: adoption by repo ────────────────────────────────────────

def chart_by_repo(df: pd.DataFrame) -> None:
    # Keep only repos with non-zero engTotal, sort by adoption desc.
    active = df[df["engTotal"] > 0].sort_values("share", ascending=True).reset_index(drop=True)
    if active.empty:
        print("(no active repos — skipping chart 2)")
        return

    fig, ax = plt.subplots(figsize=(11, max(5, 0.45 * len(active) + 2)))
    bars = ax.barh(active["repo"], active["share"], color=BOT_COLOR)
    ax.set_xlabel("Adopción (%)  =  bot / (bot + humanos)")
    ax.set_title("Adopción de hu-agent por repositorio (ventana completa)")
    ax.xaxis.set_major_formatter(ticker.PercentFormatter(decimals=1))
    ax.set_xlim(left=0, right=max(active["share"].max() * 1.2, 1.0))
    ax.grid(True, axis="x", alpha=0.25)

    for bar, (_, row) in zip(bars, active.iterrows()):
        ax.text(
            bar.get_width() + active["share"].max() * 0.01,
            bar.get_y() + bar.get_height() / 2,
            f"{int(row['prsMergedBot'])} / {int(row['engTotal'])}  ({row['share']:.1f}%)",
            va="center",
            fontsize=9,
            color="#0f172a",
        )

    fig.tight_layout()
    out = OUT_DIR / "02-adoption-by-repo.png"
    fig.savefig(out, dpi=150)
    plt.close(fig)
    print(f"wrote {out}")


# ── Chart 3: adoption by squad (from PR branch-name ticket keys) ─────

def chart_by_squad(df: pd.DataFrame) -> None:
    active = df[df["engTotal"] > 0].sort_values("share", ascending=True).reset_index(drop=True)
    if active.empty:
        print("(no active squads — skipping chart 3)")
        return

    labels = [
        f"{row['squad']}" + (f"  ·  {row['tribe']}" if row["tribe"] else "")
        for _, row in active.iterrows()
    ]

    fig, ax = plt.subplots(figsize=(11, max(5, 0.45 * len(active) + 2)))
    bars = ax.barh(labels, active["share"], color=BOT_COLOR)
    ax.set_xlabel("Adopción (%)  =  bot / (bot + humanos)")
    ax.set_title("Adopción de hu-agent por squad (ventana completa, basado en branch name)")
    ax.xaxis.set_major_formatter(ticker.PercentFormatter(decimals=1))
    ax.set_xlim(left=0, right=max(active["share"].max() * 1.2, 1.0))
    ax.grid(True, axis="x", alpha=0.25)

    for bar, (_, row) in zip(bars, active.iterrows()):
        ax.text(
            bar.get_width() + active["share"].max() * 0.01,
            bar.get_y() + bar.get_height() / 2,
            f"{int(row['prsMergedBot'])} / {int(row['engTotal'])}  ({row['share']:.1f}%)",
            va="center",
            fontsize=9,
            color="#0f172a",
        )

    fig.tight_layout()
    out = OUT_DIR / "03-adoption-by-squad.png"
    fig.savefig(out, dpi=150)
    plt.close(fig)
    print(f"wrote {out}")


# ── Stdout tables ────────────────────────────────────────────────────

def print_weekly_table(df: pd.DataFrame) -> None:
    print("\nWeekly detail (global):")
    print(f"{'Week':<12} {'Raw':>5} {'OtBot':>6} {'Humans':>7} {'Bot':>5} {'EngTot':>7} {'Adopt':>8}")
    print("-" * 57)
    for _, r in df.iterrows():
        print(
            f"{r['weekStart'].strftime('%Y-%m-%d'):<12} "
            f"{int(r['prsMergedTotal']):>5} "
            f"{int(r['prsMergedOtherBots']):>6} "
            f"{int(r['prsMergedHumans']):>7} "
            f"{int(r['prsMergedBot']):>5} "
            f"{int(r['engTotal']):>7} "
            f"{r['share']:>7.2f}%",
        )
    tot_raw = int(df["prsMergedTotal"].sum())
    tot_other = int(df["prsMergedOtherBots"].sum())
    tot_hum = int(df["prsMergedHumans"].sum())
    tot_bot = int(df["prsMergedBot"].sum())
    tot_eng = int(df["engTotal"].sum())
    share = (tot_bot / tot_eng * 100) if tot_eng else 0.0
    print("-" * 57)
    print(f"{'TOTAL':<12} {tot_raw:>5} {tot_other:>6} {tot_hum:>7} {tot_bot:>5} {tot_eng:>7} {share:>7.2f}%")


def print_rollup_table(df: pd.DataFrame, *, name_col: str, title: str) -> None:
    print(f"\n{title}:")
    active = df[df["engTotal"] > 0].sort_values("share", ascending=False).reset_index(drop=True)
    name_w = max(len(name_col), int(active[name_col].astype(str).str.len().max() or 0))
    print(f"{name_col:<{name_w}}  {'Humans':>7} {'Bot':>5} {'EngTot':>7} {'Adopt':>8}")
    print("-" * (name_w + 32))
    for _, r in active.iterrows():
        print(
            f"{str(r[name_col]):<{name_w}}  "
            f"{int(r['prsMergedHumans']):>7} "
            f"{int(r['prsMergedBot']):>5} "
            f"{int(r['engTotal']):>7} "
            f"{r['share']:>7.2f}%",
        )
    # Also list rows with no activity.
    idle = df[df["engTotal"] == 0]
    if len(idle):
        idle_names = ", ".join(idle[name_col].astype(str).tolist())
        print(f"  (sin actividad: {idle_names})")


def main() -> None:
    ensure(WEEKLY_CSV)
    ensure(REPO_CSV)
    ensure(SQUAD_CSV)

    OUT_DIR.mkdir(parents=True, exist_ok=True)

    weekly = pd.read_csv(WEEKLY_CSV, parse_dates=["weekStart"])
    weekly = weekly.sort_values("weekStart").reset_index(drop=True)
    weekly = compute_adoption(weekly)

    by_repo = pd.read_csv(REPO_CSV)
    by_repo = compute_adoption(by_repo)
    by_squad = pd.read_csv(SQUAD_CSV)
    by_squad = compute_adoption(by_squad)

    # Clean up stale chart filenames from older versions.
    for stale in ("bot-vs-others-resolved.png", "bot-vs-others-prs-merged.png",
                  "bot-vs-humans-prs-merged.png", "hu-agent-adoption.png"):
        p = OUT_DIR / stale
        if p.exists():
            p.unlink()

    chart_global(weekly)
    chart_by_repo(by_repo)
    chart_by_squad(by_squad)

    print_weekly_table(weekly)
    print_rollup_table(by_repo, name_col="repo", title="Por repositorio (ventana completa)")

    # For the squad table, also show the tribe column when available.
    print("\nPor squad (ventana completa, derivado del branch name):")
    active_sq = by_squad[by_squad["engTotal"] > 0].sort_values("share", ascending=False)
    name_w = max(6, int(active_sq["squad"].astype(str).str.len().max() or 0))
    tribe_w = max(5, int(active_sq["tribe"].astype(str).str.len().max() or 0))
    print(f"{'Squad':<{name_w}}  {'Tribe':<{tribe_w}}  {'Humans':>7} {'Bot':>5} {'EngTot':>7} {'Adopt':>8}")
    print("-" * (name_w + tribe_w + 34))
    for _, r in active_sq.iterrows():
        tribe = str(r["tribe"]) if r["tribe"] else "—"
        print(
            f"{str(r['squad']):<{name_w}}  {tribe:<{tribe_w}}  "
            f"{int(r['prsMergedHumans']):>7} "
            f"{int(r['prsMergedBot']):>5} "
            f"{int(r['engTotal']):>7} "
            f"{r['share']:>7.2f}%",
        )
    idle_sq = by_squad[by_squad["engTotal"] == 0]
    if len(idle_sq):
        print(f"  (sin actividad: {', '.join(idle_sq['squad'].astype(str).tolist())})")


if __name__ == "__main__":
    main()
