#!/usr/bin/env python3
"""Markdown-based task manager for task-execution-engine.

Manages tasks directly in markdown files using checkbox syntax:
- [ ] uncompleted task
- [x] completed task
"""

import argparse
import json
import re
import sys
from pathlib import Path
from dataclasses import dataclass, field, asdict
from typing import Optional


@dataclass
class Task:
    """Represents a task parsed from markdown."""
    title: str
    status: str  # pending, completed, failed
    priority: int = 5
    phase: str = "implementation"
    dependencies: list = field(default_factory=list)
    files: list = field(default_factory=list)
    criteria: list = field(default_factory=list)
    criteria_status: list = field(default_factory=list)  # True/False for each criterion
    line_number: int = 0
    failure_reason: str = ""


def parse_task_line(line: str) -> Optional[dict]:
    """Parse a task line like: - [ ] **Task Title** `priority:1` `phase:model`"""

    # Match checkbox task
    match = re.match(r'^- \[([ xX])\] \*\*(.+?)\*\*(.*)$', line.strip())
    if not match:
        return None

    checkbox, title, rest = match.groups()
    status = "completed" if checkbox.lower() == 'x' else "pending"

    # Check for failure marker
    if "❌" in rest or "FAILED" in rest:
        status = "failed"

    # Parse inline attributes
    priority = 5
    phase = "implementation"
    dependencies = []

    # Extract priority
    priority_match = re.search(r'`priority:(\d+)`', rest)
    if priority_match:
        priority = int(priority_match.group(1))

    # Extract phase
    phase_match = re.search(r'`phase:(\w+)`', rest)
    if phase_match:
        phase = phase_match.group(1)

    # Extract dependencies
    deps_match = re.search(r'`deps:([^`]+)`', rest)
    if deps_match:
        dependencies = [d.strip() for d in deps_match.group(1).split(',')]

    return {
        "title": title.strip(),
        "status": status,
        "priority": priority,
        "phase": phase,
        "dependencies": dependencies
    }


def parse_tasks_from_markdown(content: str) -> list[Task]:
    """Parse all tasks from markdown content."""

    lines = content.split('\n')
    tasks = []
    current_task = None
    in_task_section = False

    for i, line in enumerate(lines):
        # Check if we're in the Implementation Tasks section
        if re.match(r'^##\s+Implementation\s+Tasks', line, re.IGNORECASE):
            in_task_section = True
            continue

        # Exit task section on next ## header
        if in_task_section and re.match(r'^##\s+[^#]', line) and 'Implementation' not in line:
            in_task_section = False
            continue

        if not in_task_section:
            continue

        # Parse main task line
        task_data = parse_task_line(line)
        if task_data:
            if current_task:
                tasks.append(current_task)

            current_task = Task(
                title=task_data["title"],
                status=task_data["status"],
                priority=task_data["priority"],
                phase=task_data["phase"],
                dependencies=task_data["dependencies"],
                line_number=i + 1
            )
            continue

        # Parse task details (indented lines under a task)
        if current_task and line.strip().startswith('- '):
            stripped = line.strip()

            # Files line
            if stripped.startswith('- files:'):
                files_str = stripped.replace('- files:', '').strip()
                current_task.files = [f.strip() for f in files_str.split(',') if f.strip()]

            # Criterion line (checkbox)
            elif re.match(r'^- \[([ xX])\] ', stripped):
                checkbox_match = re.match(r'^- \[([ xX])\] (.+)$', stripped)
                if checkbox_match:
                    is_done = checkbox_match.group(1).lower() == 'x'
                    criterion = checkbox_match.group(2).strip()
                    current_task.criteria.append(criterion)
                    current_task.criteria_status.append(is_done)

            # Failure reason
            elif stripped.startswith('- reason:') or stripped.startswith('- error:'):
                current_task.failure_reason = stripped.split(':', 1)[1].strip()

    # Don't forget the last task
    if current_task:
        tasks.append(current_task)

    return tasks


def get_next_task(tasks: list[Task]) -> Optional[Task]:
    """Get the next task to execute based on priority and dependencies."""

    completed_titles = {t.title for t in tasks if t.status == "completed"}

    # Find pending tasks with satisfied dependencies
    available = []
    for task in tasks:
        if task.status != "pending":
            continue

        # Check dependencies
        deps_satisfied = all(dep in completed_titles for dep in task.dependencies)
        if deps_satisfied:
            available.append(task)

    if not available:
        return None

    # Sort by priority (lower number = higher priority)
    available.sort(key=lambda t: t.priority)
    return available[0]


def update_task_status(content: str, task_title: str, new_status: str, reason: str = "") -> str:
    """Update a task's status in the markdown content."""

    lines = content.split('\n')
    result = []
    in_target_task = False
    task_indent = 0

    for line in lines:
        # Check if this is the target task
        task_data = parse_task_line(line)
        if task_data and task_data["title"] == task_title:
            in_target_task = True
            task_indent = len(line) - len(line.lstrip())

            # Update the checkbox
            if new_status == "completed":
                line = re.sub(r'^(\s*- )\[[ ]\]', r'\1[x]', line)
                # Add completion marker if not present
                if "✅" not in line:
                    line = line.rstrip() + " ✅"
            elif new_status == "failed":
                line = re.sub(r'^(\s*- )\[[ ]\]', r'\1[x]', line)
                # Add failure marker
                if "❌" not in line:
                    line = line.rstrip() + " ❌"
            elif new_status == "pending":
                line = re.sub(r'^(\s*- )\[[xX]\]', r'\1[ ]', line)
                # Remove markers
                line = line.replace(" ✅", "").replace(" ❌", "")

            result.append(line)
            continue

        # Check if we've moved to a different task
        if task_data and task_data["title"] != task_title:
            in_target_task = False

        # Update criteria checkboxes within the task
        if in_target_task and re.match(r'^\s*- \[[ xX]\] ', line):
            current_indent = len(line) - len(line.lstrip())
            if current_indent > task_indent:
                if new_status == "completed":
                    line = re.sub(r'^(\s*- )\[[ ]\]', r'\1[x]', line)
                elif new_status == "pending":
                    line = re.sub(r'^(\s*- )\[[xX]\]', r'\1[ ]', line)

        result.append(line)

        # Add failure reason after the task line if failed
        if in_target_task and task_data and new_status == "failed" and reason:
            indent = "  " * (task_indent // 2 + 1)
            # Check if next line already has a reason
            result.append(f"{indent}- reason: {reason}")
            in_target_task = False  # Prevent adding reason multiple times

    return '\n'.join(result)


def get_status_summary(tasks: list[Task]) -> dict:
    """Get a summary of task statuses."""

    summary = {
        "total": len(tasks),
        "completed": 0,
        "pending": 0,
        "failed": 0,
        "blocked": 0
    }

    completed_titles = {t.title for t in tasks if t.status == "completed"}

    for task in tasks:
        if task.status == "completed":
            summary["completed"] += 1
        elif task.status == "failed":
            summary["failed"] += 1
        elif task.status == "pending":
            # Check if blocked by dependencies
            deps_satisfied = all(dep in completed_titles for dep in task.dependencies)
            if deps_satisfied:
                summary["pending"] += 1
            else:
                summary["blocked"] += 1

    return summary


def cmd_next(args):
    """Get the next task to execute."""
    content = Path(args.file).read_text()
    tasks = parse_tasks_from_markdown(content)

    next_task = get_next_task(tasks)

    if args.json:
        if next_task:
            print(json.dumps({
                "status": "found",
                "task": asdict(next_task)
            }, indent=2))
        else:
            summary = get_status_summary(tasks)
            print(json.dumps({
                "status": "no_tasks",
                "summary": summary,
                "message": "No pending tasks available"
            }, indent=2))
    else:
        if next_task:
            print(f"Next task: {next_task.title}")
            print(f"Priority: {next_task.priority} | Phase: {next_task.phase}")
            if next_task.files:
                print(f"Files: {', '.join(next_task.files)}")
            if next_task.criteria:
                print("Criteria:")
                for c in next_task.criteria:
                    print(f"  - {c}")
        else:
            print("No pending tasks available")


def cmd_done(args):
    """Mark a task as completed."""
    file_path = Path(args.file)
    content = file_path.read_text()

    updated = update_task_status(content, args.task, "completed")
    file_path.write_text(updated)

    if args.json:
        print(json.dumps({"status": "success", "task": args.task, "new_status": "completed"}))
    else:
        print(f"✅ Marked '{args.task}' as completed")


def cmd_fail(args):
    """Mark a task as failed."""
    file_path = Path(args.file)
    content = file_path.read_text()

    updated = update_task_status(content, args.task, "failed", args.reason or "")
    file_path.write_text(updated)

    if args.json:
        print(json.dumps({"status": "success", "task": args.task, "new_status": "failed", "reason": args.reason}))
    else:
        print(f"❌ Marked '{args.task}' as failed")
        if args.reason:
            print(f"   Reason: {args.reason}")


def cmd_status(args):
    """Show status summary."""
    content = Path(args.file).read_text()
    tasks = parse_tasks_from_markdown(content)
    summary = get_status_summary(tasks)

    if args.json:
        print(json.dumps({
            "file": args.file,
            "summary": summary,
            "tasks": [asdict(t) for t in tasks]
        }, indent=2))
    else:
        total = summary["total"]
        completed = summary["completed"]
        pct = round(completed / total * 100, 1) if total > 0 else 0

        print(f"File: {args.file}")
        print(f"Progress: {completed}/{total} ({pct}%)")
        print()
        print(f"  Completed: {summary['completed']}")
        print(f"  Pending:   {summary['pending']}")
        print(f"  Blocked:   {summary['blocked']}")
        print(f"  Failed:    {summary['failed']}")

        # Show next task
        next_task = get_next_task(tasks)
        if next_task:
            print(f"\nNext: {next_task.title}")


def cmd_list(args):
    """List all tasks."""
    content = Path(args.file).read_text()
    tasks = parse_tasks_from_markdown(content)

    if args.json:
        print(json.dumps([asdict(t) for t in tasks], indent=2))
    else:
        for task in tasks:
            status_icon = {"completed": "✅", "failed": "❌", "pending": "⬜"}.get(task.status, "?")
            print(f"{status_icon} [{task.priority}] {task.title}")


def main():
    parser = argparse.ArgumentParser(description="Markdown task manager")
    subparsers = parser.add_subparsers(dest="command", required=True)

    # next command
    next_parser = subparsers.add_parser("next", help="Get next task")
    next_parser.add_argument("--file", required=True, help="Markdown file path")
    next_parser.add_argument("--json", action="store_true", help="Output as JSON")
    next_parser.set_defaults(func=cmd_next)

    # done command
    done_parser = subparsers.add_parser("done", help="Mark task as completed")
    done_parser.add_argument("--file", required=True, help="Markdown file path")
    done_parser.add_argument("--task", required=True, help="Task title")
    done_parser.add_argument("--json", action="store_true", help="Output as JSON")
    done_parser.set_defaults(func=cmd_done)

    # fail command
    fail_parser = subparsers.add_parser("fail", help="Mark task as failed")
    fail_parser.add_argument("--file", required=True, help="Markdown file path")
    fail_parser.add_argument("--task", required=True, help="Task title")
    fail_parser.add_argument("--reason", default="", help="Failure reason")
    fail_parser.add_argument("--json", action="store_true", help="Output as JSON")
    fail_parser.set_defaults(func=cmd_fail)

    # status command
    status_parser = subparsers.add_parser("status", help="Show status summary")
    status_parser.add_argument("--file", required=True, help="Markdown file path")
    status_parser.add_argument("--json", action="store_true", help="Output as JSON")
    status_parser.set_defaults(func=cmd_status)

    # list command
    list_parser = subparsers.add_parser("list", help="List all tasks")
    list_parser.add_argument("--file", required=True, help="Markdown file path")
    list_parser.add_argument("--json", action="store_true", help="Output as JSON")
    list_parser.set_defaults(func=cmd_list)

    args = parser.parse_args()

    try:
        args.func(args)
    except FileNotFoundError:
        print(f"Error: File not found: {args.file}", file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()
