"""
Task Orchestrator - Task State Machine
Manages task lifecycle and routes to Breakpilot modules

The TaskOrchestrator is the agent orchestration layer that:
1. Receives intents from voice input
2. Creates and manages tasks
3. Routes to appropriate Breakpilot modules
4. Maintains conversation context
5. Handles follow-up queries

Note: This is a safe, internal task router with no shell access,
no email capabilities, and no external API access beyond internal services.
"""
import structlog
import httpx
from typing import Optional, List, Dict, Any
from datetime import datetime, timedelta

from config import settings
from models.task import Task, TaskState, TaskType, is_valid_transition
from models.session import TranscriptMessage

logger = structlog.get_logger(__name__)


class Intent:
    """Detected intent from voice input."""

    def __init__(
        self,
        type: TaskType,
        confidence: float,
        parameters: Dict[str, Any],
        is_actionable: bool = True,
    ):
        self.type = type
        self.confidence = confidence
        self.parameters = parameters
        self.is_actionable = is_actionable


class TaskOrchestrator:
    """
    Task orchestration and state machine management.

    Handles the full lifecycle of voice-initiated tasks:
    1. Intent -> Task creation
    2. Task queuing and execution
    3. Result handling
    4. Follow-up context

    Security: This orchestrator only routes to internal Breakpilot services
    via HTTP. It has NO access to shell commands, emails, calendars, or
    external APIs.
    """

    def __init__(self):
        self._tasks: Dict[str, Task] = {}
        self._session_tasks: Dict[str, List[str]] = {}  # session_id -> task_ids
        self._http_client: Optional[httpx.AsyncClient] = None

    async def _get_client(self) -> httpx.AsyncClient:
        """Get or create HTTP client."""
        if self._http_client is None:
            self._http_client = httpx.AsyncClient(timeout=30.0)
        return self._http_client

    async def queue_task(self, task: Task):
        """
        Queue a task for processing.
        Transitions from DRAFT to QUEUED.
        """
        if task.state != TaskState.DRAFT:
            logger.warning("Task not in DRAFT state", task_id=task.id[:8])
            return

        task.transition_to(TaskState.QUEUED, "queued_for_processing")

        # Store task
        self._tasks[task.id] = task

        # Add to session tasks
        if task.session_id not in self._session_tasks:
            self._session_tasks[task.session_id] = []
        self._session_tasks[task.session_id].append(task.id)

        logger.info(
            "Task queued",
            task_id=task.id[:8],
            type=task.type.value,
        )

        # Auto-process certain task types
        auto_process_types = [
            TaskType.STUDENT_OBSERVATION,
            TaskType.REMINDER,
            TaskType.HOMEWORK_CHECK,
        ]

        if task.type in auto_process_types:
            await self.process_task(task)

    async def process_task(self, task: Task):
        """
        Process a queued task.
        Routes to appropriate Breakpilot module.
        """
        if task.state != TaskState.QUEUED:
            logger.warning("Task not in QUEUED state", task_id=task.id[:8])
            return

        task.transition_to(TaskState.RUNNING, "processing_started")

        try:
            # Route to appropriate handler
            result = await self._route_task(task)

            # Store result
            task.result_ref = result

            # Transition to READY
            task.transition_to(TaskState.READY, "processing_complete")

            logger.info(
                "Task processed",
                task_id=task.id[:8],
                type=task.type.value,
            )

        except Exception as e:
            logger.error("Task processing failed", task_id=task.id[:8], error=str(e))
            task.error_message = str(e)
            task.transition_to(TaskState.READY, "processing_failed")

    async def _route_task(self, task: Task) -> str:
        """
        Route task to appropriate Breakpilot module.
        """
        client = await self._get_client()

        # Task type to endpoint mapping
        routes = {
            # Worksheet generation
            TaskType.WORKSHEET_GENERATE: f"{settings.klausur_service_url}/api/v1/worksheets/generate",
            TaskType.WORKSHEET_DIFFERENTIATE: f"{settings.klausur_service_url}/api/v1/worksheets/differentiate",

            # Quick activities
            TaskType.QUICK_ACTIVITY: f"{settings.klausur_service_url}/api/v1/activities/generate",
            TaskType.QUIZ_GENERATE: f"{settings.klausur_service_url}/api/v1/quizzes/generate",

            # Korrektur assistance
            TaskType.OPERATOR_CHECKLIST: f"{settings.klausur_service_url}/api/v1/corrections/operators",
            TaskType.EH_PASSAGE: f"{settings.klausur_service_url}/api/v1/corrections/eh-passage",
            TaskType.FEEDBACK_SUGGEST: f"{settings.klausur_service_url}/api/v1/corrections/feedback",
        }

        # Check if this task type needs API routing
        if task.type in routes:
            try:
                response = await client.post(
                    routes[task.type],
                    json={
                        "task_id": task.id,
                        "namespace_id": task.namespace_id,
                        "parameters": task.parameters,
                    },
                    timeout=settings.ollama_timeout,
                )
                response.raise_for_status()
                return response.json().get("result", "")
            except httpx.HTTPError as e:
                logger.error("API call failed", url=routes[task.type], error=str(e))
                raise

        # Handle local tasks (no API call needed)
        if task.type in [TaskType.STUDENT_OBSERVATION, TaskType.REMINDER, TaskType.HOMEWORK_CHECK]:
            return await self._handle_note_task(task)

        if task.type in [TaskType.CONFERENCE_TOPIC, TaskType.CORRECTION_NOTE]:
            return await self._handle_note_task(task)

        if task.type == TaskType.PARENT_LETTER:
            return await self._generate_parent_letter(task)

        if task.type == TaskType.CLASS_MESSAGE:
            return await self._generate_class_message(task)

        if task.type in [TaskType.CANVAS_EDIT, TaskType.CANVAS_LAYOUT]:
            return await self._handle_canvas_command(task)

        if task.type == TaskType.REMINDER_SCHEDULE:
            return await self._schedule_reminder(task)

        if task.type == TaskType.TASK_SUMMARY:
            return await self._generate_task_summary(task)

        logger.warning("Unknown task type", task_type=task.type.value)
        return "Task type not implemented"

    async def _handle_note_task(self, task: Task) -> str:
        """Handle simple note/observation tasks."""
        # These are stored encrypted, no further processing needed
        return "Notiz gespeichert"

    async def _generate_parent_letter(self, task: Task) -> str:
        """Generate a parent letter using LLM."""
        from services.fallback_llm_client import FallbackLLMClient

        llm = FallbackLLMClient()

        prompt = f"""Erstelle einen neutralen, professionellen Elternbrief basierend auf:
Anlass: {task.parameters.get('reason', 'Allgemeine Information')}
Kontext: {task.parameters.get('context', '')}

Der Brief soll:
- Sachlich und respektvoll formuliert sein
- Keine Schuldzuweisungen enthalten
- Konstruktiv auf Lösungen ausgerichtet sein
- In der Ich-Form aus Lehrersicht geschrieben sein

Bitte nur den Brieftext ausgeben, ohne Metakommentare."""

        result = await llm.generate(prompt)
        return result

    async def _generate_class_message(self, task: Task) -> str:
        """Generate a class message."""
        from services.fallback_llm_client import FallbackLLMClient

        llm = FallbackLLMClient()

        prompt = f"""Erstelle eine kurze Klassennachricht:
Inhalt: {task.parameters.get('content', '')}
Klasse: {task.parameters.get('class_ref', 'Klasse')}

Die Nachricht soll:
- Kurz und klar formuliert sein
- Freundlich aber verbindlich klingen
- Alle wichtigen Informationen enthalten

Nur die Nachricht ausgeben."""

        result = await llm.generate(prompt)
        return result

    async def _handle_canvas_command(self, task: Task) -> str:
        """Handle Canvas editor commands."""
        # Parse canvas commands and generate JSON instructions
        command = task.parameters.get('command', '')

        # Map natural language to Canvas actions
        canvas_actions = []

        if 'groesser' in command.lower() or 'größer' in command.lower():
            canvas_actions.append({"action": "resize", "target": "headings", "scale": 1.2})

        if 'kleiner' in command.lower():
            canvas_actions.append({"action": "resize", "target": "spacing", "scale": 0.8})

        if 'links' in command.lower():
            canvas_actions.append({"action": "move", "direction": "left"})

        if 'rechts' in command.lower():
            canvas_actions.append({"action": "move", "direction": "right"})

        if 'a4' in command.lower() or 'drucklayout' in command.lower():
            canvas_actions.append({"action": "layout", "format": "A4"})

        return str(canvas_actions)

    async def _schedule_reminder(self, task: Task) -> str:
        """Schedule a reminder for later."""
        # In production, this would use a scheduler service
        reminder_time = task.parameters.get('time', 'tomorrow')
        reminder_content = task.parameters.get('content', '')

        return f"Erinnerung geplant für {reminder_time}: {reminder_content}"

    async def _generate_task_summary(self, task: Task) -> str:
        """Generate a summary of pending tasks."""
        session_tasks = self._session_tasks.get(task.session_id, [])

        pending = []
        for task_id in session_tasks:
            t = self._tasks.get(task_id)
            if t and t.state not in [TaskState.COMPLETED, TaskState.EXPIRED]:
                pending.append(f"- {t.type.value}: {t.state.value}")

        if not pending:
            return "Keine offenen Aufgaben"

        return "Offene Aufgaben:\n" + "\n".join(pending)

    async def execute_task(self, task: Task):
        """Execute an approved task."""
        if task.state != TaskState.APPROVED:
            logger.warning("Task not approved", task_id=task.id[:8])
            return

        # Mark as completed
        task.transition_to(TaskState.COMPLETED, "user_approved")

        logger.info("Task completed", task_id=task.id[:8])

    async def get_session_tasks(
        self,
        session_id: str,
        state: Optional[TaskState] = None,
    ) -> List[Task]:
        """Get tasks for a session, optionally filtered by state."""
        task_ids = self._session_tasks.get(session_id, [])
        tasks = []

        for task_id in task_ids:
            task = self._tasks.get(task_id)
            if task:
                if state is None or task.state == state:
                    tasks.append(task)

        return tasks

    async def create_task_from_intent(
        self,
        session_id: str,
        namespace_id: str,
        intent: Intent,
        transcript: str,
    ) -> Task:
        """Create a task from a detected intent."""
        task = Task(
            session_id=session_id,
            namespace_id=namespace_id,
            type=intent.type,
            intent_text=transcript,
            parameters=intent.parameters,
        )

        await self.queue_task(task)
        return task

    async def generate_response(
        self,
        session_messages: List[TranscriptMessage],
        intent: Optional[Intent],
        namespace_id: str,
    ) -> str:
        """Generate a conversational response."""
        from services.fallback_llm_client import FallbackLLMClient

        llm = FallbackLLMClient()

        # Build conversation context
        context = "\n".join([
            f"{msg.role}: {msg.content}"
            for msg in session_messages[-5:]  # Last 5 messages
        ])

        # Generate response based on intent
        if intent:
            if intent.type in [TaskType.STUDENT_OBSERVATION, TaskType.REMINDER]:
                return "Verstanden, ich habe mir das notiert."

            if intent.type == TaskType.WORKSHEET_GENERATE:
                return "Ich erstelle das Arbeitsblatt. Das kann einen Moment dauern."

            if intent.type == TaskType.PARENT_LETTER:
                return "Ich bereite einen Elternbrief vor."

            if intent.type == TaskType.QUIZ_GENERATE:
                return "Ich generiere den Quiz. Einen Moment bitte."

        # Default: use LLM for conversational response
        prompt = f"""Du bist ein hilfreicher Assistent für Lehrer.
Konversation:
{context}

Antworte kurz und hilfreich auf die letzte Nachricht des Nutzers.
Halte die Antwort unter 50 Wörtern."""

        response = await llm.generate(prompt)
        return response
