#!/usr/bin/env python3
"""Reattribute PRs and their author events from m3taversal to the true author.

Scope (intentionally conservative):
  - branch reweave/*    -> pipeline   (system maintenance, no human author)
  - branch ingestion/*  -> pipeline   (pipeline-internal source intake)
  - branch <agent>/*    -> <agent>    (autonomous agent work)
    for agent in {leo, vida, rio, astra, clay, theseus}.

NOT in scope:
  - branch extract/*    -- proposed_by may legitimately be absent
                           (telegram source drops default to operator).

Per affected PR (atomic):
  1. UPDATE prs.submitted_by   -> target
  2. UPDATE sources.submitted_by where path = pr.source_path
  3. UPDATE contribution_events.handle for every m3ta author event on this PR
     (kind set to 'agent', since pipeline + the six agents are all kind='agent'
     per attribution.PENTAGON_AGENTS).

Idempotent. Dry-run by default; --apply commits.
Run AFTER scripts/normalize-submitted-by.py.
"""

import argparse
import os
import sqlite3
import sys
from collections import Counter

DB_PATH = os.environ.get("DB_PATH", "/opt/teleo-eval/pipeline/pipeline.db")

AGENT_PREFIXES = ("leo/", "vida/", "rio/", "astra/", "clay/", "theseus/")
PIPELINE_PREFIXES = ("reweave/", "ingestion/")


def target_for(branch):
    if not branch:
        return None
    if branch.startswith(PIPELINE_PREFIXES):
        return "pipeline"
    for prefix in AGENT_PREFIXES:
        if branch.startswith(prefix):
            return prefix.rstrip("/")
    return None


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--apply", action="store_true", help="commit changes (default: dry-run)")
    ap.add_argument("--db", default=DB_PATH)
    args = ap.parse_args()

    conn = sqlite3.connect(args.db, timeout=30)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA busy_timeout = 30000")

    mode = "APPLY" if args.apply else "DRY-RUN"
    print("DB:    {}\nMode:  {}\n".format(args.db, mode))

    rows = conn.execute("""
        SELECT number, branch, source_path
        FROM prs
        WHERE submitted_by = 'm3taversal'
          AND branch IS NOT NULL
    """).fetchall()

    pr_targets = []
    pr_counts = Counter()
    for r in rows:
        tgt = target_for(r["branch"])
        if tgt is None:
            continue
        pr_targets.append((r["number"], r["branch"], r["source_path"], tgt))
        pr_counts[tgt] += 1

    print("prs to reattribute: {}".format(len(pr_targets)))
    for tgt, n in pr_counts.most_common():
        print("  {:6d}  -> {!r}".format(n, tgt))

    src_paths = [t[2] for t in pr_targets if t[2]]
    src_count = 0
    if src_paths:
        placeholders = ",".join("?" * len(src_paths))
        src_count = conn.execute(
            "SELECT COUNT(*) FROM sources "
            "WHERE submitted_by = 'm3taversal' AND path IN ({})".format(placeholders),
            src_paths,
        ).fetchone()[0]
    print("\nsources rows that will be re-pointed: {}".format(src_count))

    pr_to_target = {p[0]: p[3] for p in pr_targets}
    events = []
    if pr_to_target:
        pr_placeholders = ",".join("?" * len(pr_to_target))
        events = conn.execute(
            "SELECT id, pr_number FROM contribution_events "
            "WHERE handle = 'm3taversal' AND role = 'author' "
            "AND pr_number IN ({})".format(pr_placeholders),
            list(pr_to_target.keys()),
        ).fetchall()
    print("contribution_events author rows to move: {}".format(len(events)))
    ev_counts = Counter(pr_to_target[e["pr_number"]] for e in events)
    for tgt, n in ev_counts.most_common():
        print("  {:6d}  events -> {!r}".format(n, tgt))

    if not args.apply:
        print("\nDry-run complete. Run with --apply to commit "
              "({} PRs + {} sources + {} events).".format(
                  len(pr_targets), src_count, len(events)))
        return 0

    pr_updated = 0
    src_updated = 0
    ev_updated = 0
    ev_collisions = 0

    try:
        for pr_num, branch, source_path, target in pr_targets:
            cur = conn.execute(
                "UPDATE prs SET submitted_by = ? "
                "WHERE number = ? AND submitted_by = 'm3taversal'",
                (target, pr_num),
            )
            pr_updated += cur.rowcount

            if source_path:
                cur = conn.execute(
                    "UPDATE sources SET submitted_by = ? "
                    "WHERE path = ? AND submitted_by = 'm3taversal'",
                    (target, source_path),
                )
                src_updated += cur.rowcount

            for ev in conn.execute(
                "SELECT id FROM contribution_events "
                "WHERE handle = 'm3taversal' AND role = 'author' AND pr_number = ?",
                (pr_num,),
            ).fetchall():
                try:
                    conn.execute(
                        "UPDATE contribution_events SET handle = ?, kind = 'agent' "
                        "WHERE id = ?",
                        (target, ev["id"]),
                    )
                    ev_updated += 1
                except sqlite3.IntegrityError:
                    conn.execute(
                        "DELETE FROM contribution_events WHERE id = ?",
                        (ev["id"],),
                    )
                    ev_collisions += 1

        conn.commit()
    except Exception:
        conn.rollback()
        raise

    print("\nCommitted.")
    print("  prs.submitted_by      moves: {}".format(pr_updated))
    print("  sources.submitted_by  moves: {}".format(src_updated))
    print("  contribution_events   moves: {}".format(ev_updated))
    print("  ce collisions deleted:       {}".format(ev_collisions))
    return 0


if __name__ == "__main__":
    sys.exit(main())
