#!/usr/bin/env python3
"""
GENESIS TASK PRIORITIZER
========================
Automatically prioritizes tasks based on complexity, dependencies, and impact.

Uses heuristics to determine optimal task execution order:
1. Simple tasks first (quick wins)
2. Blocking dependencies resolved early
3. High-impact tasks prioritized
4. Resource-intensive tasks scheduled appropriately

Usage:
    from task_prioritizer import TaskPrioritizer
    prioritizer = TaskPrioritizer()
    ordered_tasks = prioritizer.prioritize(tasks)
"""

import json
import re
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Dict, List, Optional, Any


class TaskComplexity(Enum):
    """Task complexity levels."""
    SIMPLE = 1
    MODERATE = 2
    COMPLEX = 3
    ARCHITECTURAL = 4


class TaskPriority(Enum):
    """Task priority levels."""
    CRITICAL = 1
    HIGH = 2
    MEDIUM = 3
    LOW = 4


@dataclass
class PrioritizedTask:
    """A task with computed priority metadata."""
    id: str
    title: str
    complexity: TaskComplexity
    priority: TaskPriority
    score: float
    dependencies: List[str] = field(default_factory=list)
    estimated_cost: float = 0.0
    recommended_agent: str = "gemini-2.0-flash"
    original_data: Dict = field(default_factory=dict)


class TaskPrioritizer:
    """
    Prioritizes tasks for optimal execution order.

    Scoring factors:
    - Complexity (simpler = higher priority)
    - Impact (high-impact keywords = higher priority)
    - Dependencies (fewer dependencies = higher priority)
    - Cost efficiency (lower cost = higher priority)
    """

    # Keywords that indicate high-impact tasks
    HIGH_IMPACT_KEYWORDS = [
        "critical", "urgent", "security", "bug", "fix", "broken",
        "revenue", "customer", "production", "deploy"
    ]

    # Keywords that indicate blocking/foundational tasks
    FOUNDATION_KEYWORDS = [
        "setup", "initialize", "create", "configure", "install",
        "migration", "schema", "database", "infrastructure"
    ]

    # Complexity detection keywords
    COMPLEXITY_KEYWORDS = {
        TaskComplexity.SIMPLE: [
            "rename", "update", "change", "typo", "comment", "log"
        ],
        TaskComplexity.MODERATE: [
            "implement", "add", "create", "build", "test"
        ],
        TaskComplexity.COMPLEX: [
            "refactor", "optimize", "analyze", "debug", "integrate"
        ],
        TaskComplexity.ARCHITECTURAL: [
            "architect", "design", "framework", "system", "migration"
        ]
    }

    # Agent cost estimates (per task)
    AGENT_COSTS = {
        "claude-opus": 3.0,
        "claude-sonnet": 1.0,
        "gemini-2.5-pro": 0.75,
        "gemini-2.0-flash": 0.25,
        "aiva-qwen": 0.0
    }

    def __init__(self):
        self.tasks_path = Path(__file__).parent.parent / "loop" / "tasks.json"

    def load_tasks(self) -> List[Dict]:
        """Load tasks from tasks.json."""
        if not self.tasks_path.exists():
            return []

        with open(self.tasks_path) as f:
            data = json.load(f)

        return data.get("stories", [])

    def detect_complexity(self, task: Dict) -> TaskComplexity:
        """Detect task complexity from title and description."""
        text = f"{task.get('title', '')} {task.get('description', '')}".lower()

        # Check explicit complexity field
        explicit = task.get("complexity", "").lower()
        if explicit:
            mapping = {
                "simple": TaskComplexity.SIMPLE,
                "moderate": TaskComplexity.MODERATE,
                "complex": TaskComplexity.COMPLEX,
                "architectural": TaskComplexity.ARCHITECTURAL
            }
            if explicit in mapping:
                return mapping[explicit]

        # Detect from keywords
        for complexity, keywords in self.COMPLEXITY_KEYWORDS.items():
            if any(kw in text for kw in keywords):
                return complexity

        return TaskComplexity.MODERATE

    def detect_priority(self, task: Dict) -> TaskPriority:
        """Detect task priority from content."""
        text = f"{task.get('title', '')} {task.get('description', '')}".lower()

        # High impact keywords
        if any(kw in text for kw in self.HIGH_IMPACT_KEYWORDS):
            return TaskPriority.HIGH

        # Foundation tasks are high priority
        if any(kw in text for kw in self.FOUNDATION_KEYWORDS):
            return TaskPriority.HIGH

        return TaskPriority.MEDIUM

    def detect_dependencies(self, task: Dict, all_tasks: List[Dict]) -> List[str]:
        """Detect task dependencies."""
        dependencies = []

        # Check explicit dependencies
        explicit_deps = task.get("dependencies", [])
        if explicit_deps:
            return explicit_deps

        # Heuristic: Foundation tasks are dependencies
        task_text = f"{task.get('title', '')} {task.get('description', '')}".lower()

        for other in all_tasks:
            if other.get("id") == task.get("id"):
                continue

            other_text = f"{other.get('title', '')} {other.get('description', '')}".lower()

            # If this task mentions integrating/using something another task creates
            if any(kw in other_text for kw in self.FOUNDATION_KEYWORDS):
                # Check if task references the foundation
                other_keywords = other.get("title", "").lower().split()
                if any(kw in task_text for kw in other_keywords if len(kw) > 4):
                    dependencies.append(other.get("id"))

        return dependencies

    def select_agent(self, complexity: TaskComplexity, task: Dict) -> str:
        """Select optimal agent for task."""
        # Check explicit recommendation
        recommended = task.get("recommended_agent")
        if recommended:
            return recommended

        # Select based on complexity
        agent_map = {
            TaskComplexity.SIMPLE: "aiva-qwen",
            TaskComplexity.MODERATE: "gemini-2.0-flash",
            TaskComplexity.COMPLEX: "gemini-2.5-pro",
            TaskComplexity.ARCHITECTURAL: "claude-opus"
        }

        return agent_map.get(complexity, "gemini-2.0-flash")

    def calculate_score(
        self,
        task: Dict,
        complexity: TaskComplexity,
        priority: TaskPriority,
        dependencies: List[str],
        estimated_cost: float
    ) -> float:
        """
        Calculate priority score (lower = higher priority).

        Score components:
        - Priority level (1-4, lower is better)
        - Complexity penalty (simple tasks get bonus)
        - Dependency penalty (blocked tasks penalized)
        - Cost factor (cheaper tasks slightly preferred)
        """
        score = 0.0

        # Priority (1-4 points)
        score += priority.value

        # Complexity bonus/penalty
        complexity_scores = {
            TaskComplexity.SIMPLE: -0.5,      # Bonus for quick wins
            TaskComplexity.MODERATE: 0,
            TaskComplexity.COMPLEX: 0.5,
            TaskComplexity.ARCHITECTURAL: 1.0  # Slight penalty
        }
        score += complexity_scores.get(complexity, 0)

        # Dependency penalty (2 points per dependency)
        score += len(dependencies) * 2

        # Cost factor (small influence)
        score += estimated_cost * 0.1

        return round(score, 2)

    def prioritize(self, tasks: List[Dict] = None) -> List[PrioritizedTask]:
        """
        Prioritize tasks and return ordered list.

        Args:
            tasks: Task list. If None, loads from tasks.json

        Returns:
            List of PrioritizedTask sorted by priority score
        """
        if tasks is None:
            tasks = self.load_tasks()

        # Filter to pending tasks
        pending = [t for t in tasks if not t.get("passes", False)]

        prioritized = []
        for task in pending:
            complexity = self.detect_complexity(task)
            priority = self.detect_priority(task)
            dependencies = self.detect_dependencies(task, tasks)
            agent = self.select_agent(complexity, task)
            cost = self.AGENT_COSTS.get(agent, 0.5)
            score = self.calculate_score(task, complexity, priority, dependencies, cost)

            prioritized.append(PrioritizedTask(
                id=task.get("id", "unknown"),
                title=task.get("title", "Unknown Task"),
                complexity=complexity,
                priority=priority,
                score=score,
                dependencies=dependencies,
                estimated_cost=cost,
                recommended_agent=agent,
                original_data=task
            ))

        # Sort by score (lower is higher priority)
        prioritized.sort(key=lambda t: t.score)

        return prioritized

    def get_next_task(self, tasks: List[Dict] = None) -> Optional[PrioritizedTask]:
        """Get the highest priority task that's ready to run."""
        prioritized = self.prioritize(tasks)

        if not prioritized:
            return None

        # Find first task without unmet dependencies
        completed_ids = set(
            t.get("id") for t in (tasks or self.load_tasks())
            if t.get("passes")
        )

        for task in prioritized:
            unmet = [d for d in task.dependencies if d not in completed_ids]
            if not unmet:
                return task

        # If all have unmet dependencies, return highest priority anyway
        return prioritized[0] if prioritized else None

    def display(self, tasks: List[Dict] = None):
        """Display prioritized task list."""
        prioritized = self.prioritize(tasks)

        print("\n" + "=" * 70)
        print("GENESIS TASK PRIORITIZER")
        print("=" * 70)
        print(f"Total pending tasks: {len(prioritized)}")
        print("-" * 70)

        for i, task in enumerate(prioritized, 1):
            deps = f" [deps: {', '.join(task.dependencies)}]" if task.dependencies else ""
            print(f"\n{i}. [{task.score:.1f}] {task.title}")
            print(f"   Complexity: {task.complexity.name} | Priority: {task.priority.name}")
            print(f"   Agent: {task.recommended_agent} | Est. Cost: ${task.estimated_cost:.2f}")
            if deps:
                print(f"   Dependencies: {', '.join(task.dependencies)}")

        print("\n" + "=" * 70)


def main():
    prioritizer = TaskPrioritizer()
    prioritizer.display()

    next_task = prioritizer.get_next_task()
    if next_task:
        print(f"\nNext task to execute: {next_task.title}")
        print(f"Use agent: {next_task.recommended_agent}")


if __name__ == "__main__":
    main()
