#!/usr/bin/env python3
"""Analyze the AgentHub git DAG.

Detects frontier branches (leaves with no children), displays DAG graphs,
and shows per-agent branch status for a session.

Usage:
    python dag_analyzer.py --frontier --session 20260317-143022
    python dag_analyzer.py --graph
    python dag_analyzer.py --status --session 20260317-143022
    python dag_analyzer.py --demo
"""

import argparse
import json
import os
import re
import subprocess
import sys
from datetime import datetime


def run_git(*args):
    """Run a git command and return stdout."""
    try:
        result = subprocess.run(
            ["git"] + list(args),
            capture_output=True, text=True, check=True
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Git error: {e.stderr.strip()}", file=sys.stderr)
        return ""


def get_hub_branches(session_id=None):
    """Get all hub/* branches, optionally filtered by session."""
    output = run_git("branch", "--list", "hub/*", "--format=%(refname:short)")
    if not output:
        return []
    branches = output.strip().split("\n")
    if session_id:
        prefix = f"hub/{session_id}/"
        branches = [b for b in branches if b.startswith(prefix)]
    return branches


def get_branch_commit(branch):
    """Get the commit hash for a branch."""
    return run_git("rev-parse", "--short", branch)


def get_branch_commit_count(branch, base_branch="main"):
    """Count commits ahead of base branch."""
    output = run_git("rev-list", "--count", f"{base_branch}..{branch}")
    try:
        return int(output)
    except ValueError:
        return 0


def get_branch_last_commit_date(branch):
    """Get the last commit date for a branch."""
    output = run_git("log", "-1", "--format=%ci", branch)
    if output:
        return output[:19]
    return "unknown"


def get_branch_last_commit_msg(branch):
    """Get the last commit message for a branch."""
    return run_git("log", "-1", "--format=%s", branch)


def detect_frontier(session_id=None):
    """Find frontier branches (tips with no child branches).

    A branch is on the frontier if no other hub branch contains its tip commit
    as an ancestor (i.e., it has no children in the DAG).
    """
    branches = get_hub_branches(session_id)
    if not branches:
        return []

    # Get commit hashes for all branches
    branch_commits = {}
    for b in branches:
        commit = run_git("rev-parse", b)
        if commit:
            branch_commits[b] = commit

    # A branch is frontier if its commit is not an ancestor of any other branch
    frontier = []
    for branch, commit in branch_commits.items():
        is_ancestor = False
        for other_branch, other_commit in branch_commits.items():
            if other_branch == branch:
                continue
            # Check if commit is ancestor of other_commit
            result = subprocess.run(
                ["git", "merge-base", "--is-ancestor", commit, other_commit],
                capture_output=True
            )
            if result.returncode == 0:
                is_ancestor = True
                break
        if not is_ancestor:
            frontier.append(branch)

    return frontier


def show_graph():
    """Display the git DAG graph for hub branches."""
    branches = get_hub_branches()
    if not branches:
        print("No hub/* branches found.")
        return

    # Use git log with graph for hub branches
    branch_args = [b for b in branches]
    output = run_git(
        "log", "--all", "--oneline", "--graph", "--decorate",
        "--simplify-by-decoration",
        *[f"--branches=hub/*"]
    )
    if output:
        print(output)
    else:
        print("No hub commits found.")


def show_status(session_id, output_format="table"):
    """Show per-agent branch status for a session."""
    branches = get_hub_branches(session_id)
    if not branches:
        print(f"No branches found for session {session_id}")
        return

    frontier = detect_frontier(session_id)

    # Parse agent info from branch names
    agents = []
    for branch in sorted(branches):
        # Pattern: hub/{session}/agent-{N}/attempt-{M}
        match = re.match(r"hub/[^/]+/agent-(\d+)/attempt-(\d+)", branch)
        if match:
            agent_num = int(match.group(1))
            attempt = int(match.group(2))
        else:
            agent_num = 0
            attempt = 1

        commit = get_branch_commit(branch)
        commits = get_branch_commit_count(branch)
        last_date = get_branch_last_commit_date(branch)
        last_msg = get_branch_last_commit_msg(branch)
        is_frontier = branch in frontier

        agents.append({
            "agent": agent_num,
            "attempt": attempt,
            "branch": branch,
            "commit": commit,
            "commits_ahead": commits,
            "last_update": last_date,
            "last_message": last_msg,
            "frontier": is_frontier,
        })

    if output_format == "json":
        print(json.dumps({"session": session_id, "agents": agents}, indent=2))
        return

    # Table output
    print(f"Session: {session_id}")
    print(f"Branches: {len(branches)} | Frontier: {len(frontier)}")
    print()
    header = f"{'AGENT':<8} {'BRANCH':<45} {'COMMITS':<8} {'STATUS':<10} {'LAST UPDATE':<20}"
    print(header)
    print("-" * len(header))
    for a in agents:
        status = "frontier" if a["frontier"] else "merged"
        print(f"agent-{a['agent']:<4} {a['branch']:<45} {a['commits_ahead']:<8} {status:<10} {a['last_update']:<20}")


def run_demo():
    """Show demo output."""
    print("=" * 60)
    print("AgentHub DAG Analyzer — Demo Mode")
    print("=" * 60)
    print()

    print("--- Frontier Detection ---")
    print("Frontier branches (leaves with no children):")
    print("  hub/20260317-143022/agent-1/attempt-1  (3 commits ahead)")
    print("  hub/20260317-143022/agent-2/attempt-1  (5 commits ahead)")
    print("  hub/20260317-143022/agent-3/attempt-1  (2 commits ahead)")
    print()

    print("--- Session Status ---")
    print("Session: 20260317-143022")
    print("Branches: 3 | Frontier: 3")
    print()
    header = f"{'AGENT':<8} {'BRANCH':<45} {'COMMITS':<8} {'STATUS':<10} {'LAST UPDATE':<20}"
    print(header)
    print("-" * len(header))
    print(f"{'agent-1':<8} {'hub/20260317-143022/agent-1/attempt-1':<45} {'3':<8} {'frontier':<10} {'2026-03-17 14:35:10':<20}")
    print(f"{'agent-2':<8} {'hub/20260317-143022/agent-2/attempt-1':<45} {'5':<8} {'frontier':<10} {'2026-03-17 14:36:45':<20}")
    print(f"{'agent-3':<8} {'hub/20260317-143022/agent-3/attempt-1':<45} {'2':<8} {'frontier':<10} {'2026-03-17 14:34:22':<20}")
    print()

    print("--- DAG Graph ---")
    print("* abc1234 (hub/20260317-143022/agent-2/attempt-1) Replaced O(n²) with hash map")
    print("* def5678 Added benchmark tests")
    print("| * ghi9012 (hub/20260317-143022/agent-1/attempt-1) Added caching layer")
    print("| * jkl3456 Refactored data access")
    print("|/")
    print("| * mno7890 (hub/20260317-143022/agent-3/attempt-1) Minor optimizations")
    print("|/")
    print("* pqr1234 (dev) Base commit")


def main():
    parser = argparse.ArgumentParser(
        description="Analyze the AgentHub git DAG"
    )
    parser.add_argument("--frontier", action="store_true",
                        help="List frontier branches (leaves with no children)")
    parser.add_argument("--graph", action="store_true",
                        help="Show ASCII DAG graph for hub branches")
    parser.add_argument("--status", action="store_true",
                        help="Show per-agent branch status")
    parser.add_argument("--session", type=str,
                        help="Filter by session ID")
    parser.add_argument("--format", choices=["table", "json"], default="table",
                        help="Output format (default: table)")
    parser.add_argument("--demo", action="store_true",
                        help="Show demo output")
    args = parser.parse_args()

    if args.demo:
        run_demo()
        return

    if not any([args.frontier, args.graph, args.status]):
        parser.print_help()
        return

    if args.frontier:
        frontier = detect_frontier(args.session)
        if args.format == "json":
            print(json.dumps({"frontier": frontier}, indent=2))
        else:
            if frontier:
                print("Frontier branches:")
                for b in frontier:
                    print(f"  {b}")
            else:
                print("No frontier branches found.")
        print()

    if args.graph:
        show_graph()
        print()

    if args.status:
        if not args.session:
            print("Error: --session required with --status", file=sys.stderr)
            sys.exit(1)
        show_status(args.session, args.format)


if __name__ == "__main__":
    main()
