"""Action approval system for tasks requiring user confirmation."""

from __future__ import annotations

import time
import uuid
from dataclasses import dataclass, field
from enum import StrEnum
from typing import Any

from agent_scout.events import Event, EventBus, EventType


class ApprovalStatus(StrEnum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"
    MODIFIED = "modified"
    EXPIRED = "expired"


@dataclass
class ApprovalRequest:
    """A request for user approval of an agent action."""

    id: str = field(default_factory=lambda: uuid.uuid4().hex[:12])
    task_id: str = ""
    action_type: str = ""  # e.g. "purchase", "book", "send_email"
    description: str = ""
    estimated_cost: float = 0.0
    details: dict[str, Any] = field(default_factory=dict)
    status: ApprovalStatus = ApprovalStatus.PENDING
    response_message: str = ""
    modifications: dict[str, Any] = field(default_factory=dict)
    created_at: float = field(default_factory=time.time)
    resolved_at: float = 0.0
    timeout_seconds: float = 300.0  # 5 minutes default


@dataclass
class AuditEntry:
    """Audit log entry for an approval decision."""

    request_id: str
    task_id: str
    action_type: str
    status: ApprovalStatus
    timestamp: float = field(default_factory=time.time)
    details: dict[str, Any] = field(default_factory=dict)


class ApprovalManager:
    """Manages action approval requests and audit logging.

    When the agent wants to perform a real-world action (booking,
    purchasing, etc.), it creates an approval request. The user
    can approve, reject, or modify the request.
    """

    def __init__(self, event_bus: EventBus | None = None) -> None:
        self._bus = event_bus
        self._pending: dict[str, ApprovalRequest] = {}
        self._audit_log: list[AuditEntry] = []

    async def request_approval(
        self,
        task_id: str,
        action_type: str,
        description: str,
        estimated_cost: float = 0.0,
        details: dict[str, Any] | None = None,
        timeout_seconds: float = 300.0,
    ) -> ApprovalRequest:
        """Create a new approval request and emit event."""
        request = ApprovalRequest(
            task_id=task_id,
            action_type=action_type,
            description=description,
            estimated_cost=estimated_cost,
            details=details or {},
            timeout_seconds=timeout_seconds,
        )

        self._pending[request.id] = request

        if self._bus:
            await self._bus.emit(
                Event(
                    event_type=EventType.USER_APPROVAL_REQUESTED,
                    data={
                        "request_id": request.id,
                        "action_type": action_type,
                        "description": description,
                        "estimated_cost": estimated_cost,
                        "details": details or {},
                    },
                    task_id=task_id,
                )
            )

        return request

    async def approve(
        self,
        request_id: str,
        message: str = "",
    ) -> ApprovalRequest | None:
        """Approve a pending request."""
        request = self._pending.get(request_id)
        if not request or request.status != ApprovalStatus.PENDING:
            return None

        request.status = ApprovalStatus.APPROVED
        request.response_message = message
        request.resolved_at = time.time()

        self._log_audit(request)
        del self._pending[request_id]

        if self._bus:
            await self._bus.emit(
                Event(
                    event_type=EventType.USER_APPROVAL_RECEIVED,
                    data={
                        "request_id": request_id,
                        "status": ApprovalStatus.APPROVED.value,
                        "message": message,
                    },
                    task_id=request.task_id,
                )
            )

        return request

    async def reject(
        self,
        request_id: str,
        message: str = "",
    ) -> ApprovalRequest | None:
        """Reject a pending request."""
        request = self._pending.get(request_id)
        if not request or request.status != ApprovalStatus.PENDING:
            return None

        request.status = ApprovalStatus.REJECTED
        request.response_message = message
        request.resolved_at = time.time()

        self._log_audit(request)
        del self._pending[request_id]

        if self._bus:
            await self._bus.emit(
                Event(
                    event_type=EventType.USER_APPROVAL_RECEIVED,
                    data={
                        "request_id": request_id,
                        "status": ApprovalStatus.REJECTED.value,
                        "message": message,
                    },
                    task_id=request.task_id,
                )
            )

        return request

    async def modify(
        self,
        request_id: str,
        modifications: dict[str, Any],
        message: str = "",
    ) -> ApprovalRequest | None:
        """Approve with modifications."""
        request = self._pending.get(request_id)
        if not request or request.status != ApprovalStatus.PENDING:
            return None

        request.status = ApprovalStatus.MODIFIED
        request.modifications = modifications
        request.response_message = message
        request.resolved_at = time.time()

        self._log_audit(request)
        del self._pending[request_id]

        if self._bus:
            await self._bus.emit(
                Event(
                    event_type=EventType.USER_APPROVAL_RECEIVED,
                    data={
                        "request_id": request_id,
                        "status": ApprovalStatus.MODIFIED.value,
                        "modifications": modifications,
                        "message": message,
                    },
                    task_id=request.task_id,
                )
            )

        return request

    def check_expired(self) -> list[ApprovalRequest]:
        """Check for and expire timed-out requests."""
        now = time.time()
        expired: list[ApprovalRequest] = []

        for req_id, request in list(self._pending.items()):
            if now - request.created_at > request.timeout_seconds:
                request.status = ApprovalStatus.EXPIRED
                request.resolved_at = now
                self._log_audit(request)
                del self._pending[req_id]
                expired.append(request)

        return expired

    def get_pending(self) -> list[ApprovalRequest]:
        """Get all pending approval requests."""
        return list(self._pending.values())

    def get_audit_log(
        self,
        task_id: str | None = None,
        limit: int = 50,
    ) -> list[AuditEntry]:
        """Get audit log entries, optionally filtered by task."""
        entries = self._audit_log
        if task_id:
            entries = [e for e in entries if e.task_id == task_id]
        return entries[-limit:]

    def _log_audit(self, request: ApprovalRequest) -> None:
        """Add an entry to the audit log."""
        entry = AuditEntry(
            request_id=request.id,
            task_id=request.task_id,
            action_type=request.action_type,
            status=request.status,
            details={
                "description": request.description,
                "estimated_cost": request.estimated_cost,
                "response": request.response_message,
                "modifications": request.modifications,
            },
        )
        self._audit_log.append(entry)
