#!/usr/bin/env python3
"""
Analyze dependency DAG for critical path and parallelism metrics.

Usage:
    python3 analyze-dag.py spoq/epics/epic-name
    python3 analyze-dag.py spoq/epics/epic-name --agents 4
"""

import sys
from pathlib import Path

# Add lib directory to path for imports
lib_path = Path(__file__).parent.parent.parent / "lib"
sys.path.insert(0, str(lib_path))

from epic_utils import (
    load_tasks,
    compute_waves,
    group_waves,
    compute_critical_path,
    compute_parallelism_metrics,
    get_task_duration,
    calculate_pert_estimate
)


def analyze_epic(epic_path: Path, num_agents: int = 4) -> dict:
    """
    Perform comprehensive DAG analysis on an epic.

    Returns dict with critical path, parallelism metrics, and estimates.
    """
    tasks = load_tasks(epic_path)
    valid_tasks = [t for t in tasks if "error" not in t]

    if not valid_tasks:
        return {"error": "No valid tasks found"}

    # Compute waves
    try:
        waves = compute_waves(valid_tasks)
        wave_groups = group_waves(waves)
    except ValueError as e:
        return {"error": f"Wave computation failed: {e}"}

    # Compute critical path
    critical_path, critical_duration = compute_critical_path(valid_tasks, waves)

    # Compute parallelism metrics
    parallelism = compute_parallelism_metrics(valid_tasks, waves)

    # Calculate total effort
    total_effort = sum(get_task_duration(t) for t in valid_tasks)
    total_pert = sum(calculate_pert_estimate(t) for t in valid_tasks)

    # Estimate wall-clock time with N agents
    # Simplified model: total_effort / num_agents, bounded by critical path
    parallel_estimate = max(critical_duration, total_effort / num_agents)

    # Calculate speedup
    speedup = total_effort / critical_duration if critical_duration > 0 else 1.0

    # Build task map for titles
    task_map = {t["id"]: t for t in valid_tasks}

    return {
        "epic_name": epic_path.name,
        "total_tasks": len(valid_tasks),
        "critical_path": {
            "tasks": critical_path,
            "duration_hours": critical_duration,
            "task_details": [
                {
                    "id": tid,
                    "title": task_map.get(tid, {}).get("title", tid),
                    "duration": get_task_duration(task_map.get(tid, {}))
                }
                for tid in critical_path
            ]
        },
        "parallelism": {
            "wave_count": parallelism["phase_count"],
            "wave0_tasks": parallelism["phase1_tasks"],
            "wave0_ratio": parallelism["phase1_ratio"],
            "max_parallelism": parallelism["max_parallelism"],
            "waves": {
                wave: tasks
                for wave, tasks in sorted(parallelism["phases"].items())
            }
        },
        "effort": {
            "total_hours": total_effort,
            "total_pert_hours": total_pert,
            "critical_path_hours": critical_duration,
            "parallel_estimate_hours": parallel_estimate,
            "speedup_factor": speedup,
            "num_agents": num_agents
        }
    }


def format_report(analysis: dict) -> str:
    """Format analysis results as human-readable report."""
    lines = []

    if "error" in analysis:
        return f"Error: {analysis['error']}"

    lines.append("=" * 60)
    lines.append(f"EPIC DAG ANALYSIS: {analysis['epic_name']}")
    lines.append("=" * 60)
    lines.append("")

    # Critical Path
    cp = analysis["critical_path"]
    lines.append("CRITICAL PATH")
    lines.append("-" * 40)
    path_str = " -> ".join(cp["tasks"])
    lines.append(f"  Path: {path_str}")
    lines.append(f"  Duration: {cp['duration_hours']:.1f} hours")
    lines.append(f"  Length: {len(cp['tasks'])} tasks")
    lines.append("")
    lines.append("  Task breakdown:")
    for task in cp["task_details"]:
        lines.append(f"    {task['id']}: {task['title']} ({task['duration']:.1f}h)")
    lines.append("")

    # Parallelism
    par = analysis["parallelism"]
    lines.append("PARALLELISM METRICS")
    lines.append("-" * 40)
    lines.append(f"  Total tasks: {analysis['total_tasks']}")
    lines.append(f"  Wave count: {par['wave_count']}")
    lines.append(f"  Wave 0 tasks: {par['wave0_tasks']} ({par['wave0_ratio']*100:.0f}%)")
    lines.append(f"  Max parallelism: {par['max_parallelism']} agents")
    lines.append("")
    lines.append("  Wave breakdown:")
    for wave, tasks in par["waves"].items():
        lines.append(f"    Wave {wave}: {', '.join(tasks)}")
    lines.append("")

    # Effort Estimates
    effort = analysis["effort"]
    lines.append("EFFORT ESTIMATES")
    lines.append("-" * 40)
    lines.append(f"  Total effort: {effort['total_hours']:.1f} hours")
    lines.append(f"  PERT estimate: {effort['total_pert_hours']:.1f} hours")
    lines.append(f"  Critical path: {effort['critical_path_hours']:.1f} hours")
    lines.append(f"  With {effort['num_agents']} agents: ~{effort['parallel_estimate_hours']:.1f} hours")
    lines.append(f"  Speedup factor: {effort['speedup_factor']:.1f}x")
    lines.append("")

    # Summary
    lines.append("SUMMARY")
    lines.append("-" * 40)
    if par["wave0_ratio"] >= 0.25:
        lines.append("  [OK] Good parallelism (Wave 0 >= 25%)")
    else:
        lines.append("  [!!] Low parallelism (Wave 0 < 25%) - consider restructuring")

    if len(cp["tasks"]) <= analysis["total_tasks"] * 0.4:
        lines.append("  [OK] Critical path is reasonable (<40% of tasks)")
    else:
        lines.append("  [!!] Long critical path (>40% of tasks) - limits speedup")

    if effort["speedup_factor"] >= 2.0:
        lines.append(f"  [OK] Good speedup potential ({effort['speedup_factor']:.1f}x)")
    else:
        lines.append(f"  [!!] Limited speedup ({effort['speedup_factor']:.1f}x) - too sequential")

    return "\n".join(lines)


def main():
    if len(sys.argv) < 2:
        print("Usage: analyze-dag.py <epic-path> [--agents N]", file=sys.stderr)
        sys.exit(1)

    epic_path = Path(sys.argv[1])
    num_agents = 4  # Default

    # Parse --agents flag
    for i, arg in enumerate(sys.argv):
        if arg == "--agents" and i + 1 < len(sys.argv):
            try:
                num_agents = int(sys.argv[i + 1])
            except ValueError:
                print("Error: --agents requires an integer", file=sys.stderr)
                sys.exit(1)

    if not epic_path.exists():
        print(f"Error: Epic path not found: {epic_path}", file=sys.stderr)
        sys.exit(1)

    analysis = analyze_epic(epic_path, num_agents)
    report = format_report(analysis)
    print(report)

    if "error" in analysis:
        sys.exit(1)


if __name__ == "__main__":
    main()
