#!/usr/bin/env python3
"""
Merge Conflict Detector — catches sibling PRs that will conflict.

When multiple PRs branch from the same base commit and touch the same files,
merging one invalidates the others. This script detects that pattern
before it creates a rebase cascade.

Usage:
    python3 conflict_detector.py                  # Check all repos
    python3 conflict_detector.py --repo OWNER/REPO # Check one repo

Environment:
    GITEA_URL   — Gitea instance URL
    GITEA_TOKEN — API token
"""
import os
import sys
import json
import urllib.request
from collections import defaultdict

GITEA_URL = os.environ.get("GITEA_URL", "https://forge.alexanderwhitestone.com")
GITEA_TOKEN = os.environ.get("GITEA_TOKEN", "")

REPOS = [
    "Timmy_Foundation/the-nexus",
    "Timmy_Foundation/timmy-config",
    "Timmy_Foundation/timmy-home",
    "Timmy_Foundation/fleet-ops",
    "Timmy_Foundation/hermes-agent",
    "Timmy_Foundation/the-beacon",
]

def api(path):
    url = f"{GITEA_URL}/api/v1{path}"
    req = urllib.request.Request(url)
    if GITEA_TOKEN:
        req.add_header("Authorization", f"token {GITEA_TOKEN}")
    try:
        with urllib.request.urlopen(req, timeout=15) as resp:
            return json.loads(resp.read())
    except Exception:
        return []

def check_repo(repo):
    """Find sibling PRs that touch the same files."""
    prs = api(f"/repos/{repo}/pulls?state=open&limit=50")
    if not prs:
        return []
    
    # Group PRs by base commit
    by_base = defaultdict(list)
    for pr in prs:
        base_sha = pr.get("merge_base", pr.get("base", {}).get("sha", "unknown"))
        by_base[base_sha].append(pr)
    
    conflicts = []
    
    for base_sha, siblings in by_base.items():
        if len(siblings) < 2:
            continue
        
        # Get files for each sibling
        file_map = {}
        for pr in siblings:
            files = api(f"/repos/{repo}/pulls/{pr['number']}/files")
            if files:
                file_map[pr['number']] = set(f['filename'] for f in files)
        
        # Find overlapping file sets
        pr_nums = list(file_map.keys())
        for i in range(len(pr_nums)):
            for j in range(i+1, len(pr_nums)):
                a, b = pr_nums[i], pr_nums[j]
                overlap = file_map[a] & file_map[b]
                if overlap:
                    conflicts.append({
                        "repo": repo,
                        "pr_a": a,
                        "pr_b": b,
                        "base": base_sha[:8],
                        "files": sorted(overlap),
                        "title_a": next(p["title"] for p in siblings if p["number"] == a),
                        "title_b": next(p["title"] for p in siblings if p["number"] == b),
                    })
    
    return conflicts

def main():
    repos = REPOS
    if "--repo" in sys.argv:
        idx = sys.argv.index("--repo") + 1
        if idx < len(sys.argv):
            repos = [sys.argv[idx]]
    
    all_conflicts = []
    for repo in repos:
        conflicts = check_repo(repo)
        all_conflicts.extend(conflicts)
    
    if not all_conflicts:
        print("No sibling PR conflicts detected. Queue is clean.")
        return 0
    
    print(f"Found {len(all_conflicts)} potential merge conflicts:")
    print()
    for c in all_conflicts:
        print(f"  {c['repo']}:")
        print(f"    PR #{c['pr_a']} vs #{c['pr_b']} (base: {c['base']})")
        print(f"    #{c['pr_a']}: {c['title_a'][:60]}")
        print(f"    #{c['pr_b']}: {c['title_b'][:60]}")
        print(f"    Overlapping files: {', '.join(c['files'])}")
        print(f"    → Merge one first, then rebase the other.")
        print()
    
    return 1

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