"""
Intent Scheduler

Priority queue-based scheduler for managing intent execution order.
"""

import heapq
import threading
from typing import List, Optional
from dataclasses import dataclass, field
import time

from altrus.core.intent.intent import Intent, IntentPriority


@dataclass(order=True)
class ScheduledIntent:
    """Wrapper for intents in the priority queue"""
    priority: int = field(compare=True)
    submission_time: float = field(compare=True)
    intent: Intent = field(compare=False)

    @classmethod
    def create(cls, intent: Intent):
        """Create a ScheduledIntent from an Intent"""
        # Negate priority for max-heap behavior (higher priority = lower number)
        priority_values = {
            IntentPriority.LOW: 3,
            IntentPriority.NORMAL: 2,
            IntentPriority.HIGH: 1,
            IntentPriority.CRITICAL: 0
        }
        # Ensure intent.priority is an IntentPriority enum, not a dict/string
        p_val = intent.priority
        if not isinstance(p_val, IntentPriority):
            try:
                p_val = IntentPriority(p_val)
            except:
                p_val = IntentPriority.NORMAL

        return cls(
            priority=priority_values.get(p_val, 2),
            submission_time=time.time(),
            intent=intent
        )


class IntentScheduler:
    """
    Priority queue-based scheduler for intents.

    Features:
    - Priority-based ordering (CRITICAL > HIGH > NORMAL > LOW)
    - FIFO within same priority level
    - Concurrent intent limit enforcement
    - Queue depth metrics
    """

    def __init__(self, max_concurrent: int = 1):
        self._queue: List[ScheduledIntent] = []
        self._lock = threading.Lock()
        self._max_concurrent = max_concurrent
        self._executing_count = 0

        # Metrics
        self._total_queued = 0
        self._total_dequeued = 0
        self._max_queue_depth = 0

    def enqueue(self, intent: Intent):
        """Add intent to the priority queue"""
        with self._lock:
            scheduled = ScheduledIntent.create(intent)
            heapq.heappush(self._queue, scheduled)

            self._total_queued += 1
            current_depth = len(self._queue)
            if current_depth > self._max_queue_depth:
                self._max_queue_depth = current_depth

    def dequeue(self) -> Optional[Intent]:
        """
        Remove and return highest priority intent if execution slot available.

        Returns:
            Intent if available and slot free, None otherwise
        """
        with self._lock:
            # Check if we can execute more intents
            if self._executing_count >= self._max_concurrent:
                return None

            # Check if queue has intents
            if not self._queue:
                return None

            scheduled = heapq.heappop(self._queue)
            self._executing_count += 1
            self._total_dequeued += 1

            return scheduled.intent

    def mark_complete(self):
        """Mark an intent as complete, freeing up execution slot"""
        with self._lock:
            if self._executing_count > 0:
                self._executing_count -= 1

    def peek(self) -> Optional[Intent]:
        """View highest priority intent without removing it"""
        with self._lock:
            if not self._queue:
                return None
            return self._queue[0].intent

    def get_queue_depth(self) -> int:
        """Get current number of queued intents"""
        with self._lock:
            return len(self._queue)

    def get_executing_count(self) -> int:
        """Get number of currently executing intents"""
        with self._lock:
            return self._executing_count

    def get_metrics(self) -> dict:
        """Get scheduler metrics"""
        with self._lock:
            avg_wait_time = 0.0
            if self._queue:
                total_wait = sum(
                    time.time() - scheduled.submission_time
                    for scheduled in self._queue
                )
                avg_wait_time = total_wait / len(self._queue)

            return {
                "queue_depth": len(self._queue),
                "executing_count": self._executing_count,
                "total_queued": self._total_queued,
                "total_dequeued": self._total_dequeued,
                "max_queue_depth": self._max_queue_depth,
                "avg_wait_time_seconds": avg_wait_time,
                "max_concurrent": self._max_concurrent
            }

    def clear(self):
        """Clear all queued intents (for testing/shutdown)"""
        with self._lock:
            self._queue.clear()
            self._executing_count = 0
