"""
Planner Agent - Creates implementation plans from requirements.
"""

import json
import re
import logging
from typing import Optional, Dict, Any, List

from ..base import Agent, AgentContext, AgentResult
from ..registry import agent
from ..task import Task, Plan, TaskPriority
from ..events import EventEmitter, Event


logger = logging.getLogger(__name__)


@agent(name="planner", role="Software Architect and Project Planner")
class Planner(Agent):
    """
    Agent that analyzes requirements and creates a structured implementation plan.
    
    The Planner breaks down requirements into actionable tasks with:
    - Clear descriptions
    - File specifications
    - Dependencies
    - Acceptance criteria
    """
    
    name = "planner"
    role = "Software Architect and Project Planner"
    
    system_prompt = '''You are the Planner Agent, an expert software architect.

Your job is to analyze requirements and create a detailed, actionable implementation plan.

CRITICAL RULES:
1. Break down requirements into SMALL, SPECIFIC tasks
2. Each task must be completable in a single coding session
3. Tasks must have clear acceptance criteria
4. Specify exact files to create or modify
5. Define dependencies between tasks
6. Order tasks logically (foundations first)

OUTPUT FORMAT - Respond with ONLY valid JSON:
{
    "project_name": "Short project name",
    "description": "Brief description",
    "tasks": [
        {
            "id": "task_1",
            "title": "Short task title",
            "description": "Detailed description of what to implement",
            "files_to_create": ["main.py", "config.py"],
            "files_to_modify": [],
            "depends_on": [],
            "priority": 3,
            "acceptance_criteria": [
                "Criterion 1",
                "Criterion 2"
            ],
            "verification_command": "python main.py"
        }
    ],
    "final_verification": {
        "command": "python main.py",
        "expected": "Description of expected behavior"
    }
}

TASK GUIDELINES:
- Task 1 should always be project setup (create main entry point)
- Group related functionality into single tasks
- Each task should produce RUNNABLE code
- Include proper error handling tasks
- Final task should verify everything works together

IMPORTANT:
- Response must be ONLY JSON, no markdown code blocks
- All code must be complete and runnable
- Include if __name__ == '__main__' for Python entry points'''

    def __init__(
        self,
        llm=None,
        model: Optional[str] = None,
        events: Optional[EventEmitter] = None,
        **kwargs
    ):
        super().__init__(llm=llm, model=model, **kwargs)
        self.events = events
    
    def execute(self, context: AgentContext) -> AgentResult:
        """Create an implementation plan from requirements."""
        requirements = context.get("requirements", "")
        
        if not requirements:
            return AgentResult.fail("No requirements provided")
        
        self._logger.info("Creating implementation plan...")
        
        if self.events:
            self.events.emit(Event.PLANNING_START, requirements=requirements[:100])
        
        try:
            # Query LLM for plan
            prompt = self._build_prompt(requirements)
            response = self.query_llm(prompt)
            
            # Parse response
            plan_data = self._parse_response(response)
            
            if not plan_data:
                return AgentResult.fail("Failed to parse plan from LLM response")
            
            # Create Plan object
            plan = self._create_plan(plan_data)
            
            self._logger.info(f"Created plan with {len(plan.tasks)} tasks")
            
            if self.events:
                self.events.emit(
                    Event.PLANNING_COMPLETE,
                    plan_name=plan.name,
                    task_count=len(plan.tasks)
                )
            
            return AgentResult.ok({
                "plan": plan.to_dict(),
                "task_count": len(plan.tasks),
            })
            
        except Exception as e:
            self._logger.error(f"Planning failed: {e}")
            return AgentResult.fail(str(e))
    
    def _build_prompt(self, requirements: str) -> str:
        """Build the planning prompt."""
        return f"""Analyze these requirements and create an implementation plan:

## Requirements
{requirements}

Create a detailed plan with tasks that can be executed by a Coder agent.
Each task should result in working, runnable code.

Respond with ONLY valid JSON as specified in your instructions."""
    
    def _parse_response(self, response: str) -> Optional[Dict[str, Any]]:
        """Parse JSON from LLM response."""
        try:
            return json.loads(response)
        except json.JSONDecodeError:
            # Try to extract JSON
            json_match = re.search(r'\{[\s\S]*\}', response)
            if json_match:
                try:
                    return json.loads(json_match.group())
                except json.JSONDecodeError:
                    pass
            
            self._logger.error("Failed to parse plan JSON")
            return None
    
    def _create_plan(self, data: Dict[str, Any]) -> Plan:
        """Create a Plan from parsed data."""
        plan = Plan(
            name=data.get("project_name", "Implementation Plan"),
            description=data.get("description", ""),
            metadata={
                "final_verification": data.get("final_verification", {})
            }
        )
        
        priority_map = {1: TaskPriority.LOW, 2: TaskPriority.MEDIUM, 3: TaskPriority.HIGH, 4: TaskPriority.CRITICAL}
        
        for task_data in data.get("tasks", []):
            task = Task(
                id=task_data.get("id", f"task_{len(plan.tasks) + 1}"),
                title=task_data.get("title", "Untitled Task"),
                description=task_data.get("description", ""),
                files_to_create=task_data.get("files_to_create", []),
                files_to_modify=task_data.get("files_to_modify", []),
                depends_on=task_data.get("depends_on", []),
                priority=priority_map.get(task_data.get("priority", 2), TaskPriority.MEDIUM),
                metadata={
                    "acceptance_criteria": task_data.get("acceptance_criteria", []),
                    "verification_command": task_data.get("verification_command", ""),
                }
            )
            plan.add_task(task)
        
        return plan
