"""Human-in-the-loop pre-trade approval workflow."""

from __future__ import annotations

import logging
import time
import uuid
from typing import Any

from horizon.compliance._audit import AuditTrail
from horizon.compliance._store import ComplianceStore
from horizon.compliance._types import (
    ApprovalRequest, ApprovalStatus, AuditEventType,
    ComplianceAction, ComplianceConfig,
)

logger = logging.getLogger("horizon.compliance.approval")


class ApprovalGateway:
    """Pre-trade approval workflow for orders exceeding thresholds.

    Orders above approval_threshold_notional are held in a pending queue.
    A reviewer (compliance officer or risk manager) must approve or deny
    them. Expired requests are auto-denied.

    The strategy loop is never blocked -- orders that need approval are
    queued and submitted asynchronously when approved.
    """

    def __init__(
        self,
        config: ComplianceConfig,
        store: ComplianceStore,
        audit: AuditTrail,
    ) -> None:
        self._config = config
        self._store = store
        self._audit = audit
        self._approved_queue: list[dict[str, Any]] = []

    def check_order(
        self,
        order_data: dict[str, Any],
        actor: str,
        market_id: str,
    ) -> ComplianceAction:
        """Check if an order requires approval.

        Args:
            order_data: Serialized order data (price, size, side, etc.)
            actor: Identity of the requester
            market_id: Target market

        Returns:
            APPROVED if auto-approved, PENDING_APPROVAL if held for review.
        """
        if not self._config.approval_enabled:
            return ComplianceAction.APPROVED

        price = float(order_data.get("price", 0.0))
        size = float(order_data.get("size", 0.0))
        notional = price * size

        if notional < self._config.approval_threshold_notional:
            return ComplianceAction.APPROVED

        # Create approval request
        now = time.time()
        request = ApprovalRequest(
            request_id=uuid.uuid4().hex,
            order_data=order_data,
            market_id=market_id,
            requester=actor,
            reason=f"Notional ${notional:,.2f} exceeds threshold ${self._config.approval_threshold_notional:,.2f}",
            timestamp=now,
            expires_at=now + self._config.approval_timeout_secs,
        )

        self._store.insert_approval(request)
        self._audit.record(
            AuditEventType.APPROVAL_REQUESTED, actor,
            {
                "request_id": request.request_id,
                "notional": notional,
                "threshold": self._config.approval_threshold_notional,
                "expires_at": request.expires_at,
            },
            market_id=market_id,
        )

        # Notify via callback if configured
        if self._config.approval_callback is not None:
            try:
                self._config.approval_callback(request)
            except Exception as e:
                logger.warning("Approval callback failed: %s", e)

        logger.info(
            "Order pending approval: %s (notional=$%.2f, expires in %.0fs)",
            request.request_id[:12], notional, self._config.approval_timeout_secs,
        )

        return ComplianceAction.PENDING_APPROVAL

    def approve(self, request_id: str, reviewer: str, notes: str = "") -> bool:
        """Approve a pending order. Returns True if the request was found and approved."""
        req = self._store.get_approval(request_id)
        if req is None:
            logger.warning("Approval request %s not found", request_id)
            return False
        if req.status != ApprovalStatus.PENDING:
            logger.warning("Approval request %s is %s, not pending", request_id, req.status.value)
            return False

        now = time.time()
        if now > req.expires_at:
            self._store.update_approval_status(request_id, ApprovalStatus.EXPIRED, reviewer, "Expired before review")
            self._audit.record(
                AuditEventType.APPROVAL_EXPIRED, "system",
                {"request_id": request_id},
                market_id=req.market_id,
            )
            return False

        self._store.update_approval_status(request_id, ApprovalStatus.APPROVED, reviewer, notes)
        self._audit.record(
            AuditEventType.APPROVAL_GRANTED, reviewer,
            {"request_id": request_id, "notes": notes},
            market_id=req.market_id,
        )
        self._approved_queue.append(req.order_data)

        logger.info("Order approved by %s: %s", reviewer, request_id[:12])
        return True

    def deny(self, request_id: str, reviewer: str, notes: str = "") -> bool:
        """Deny a pending order. Returns True if found and denied."""
        req = self._store.get_approval(request_id)
        if req is None or req.status != ApprovalStatus.PENDING:
            return False

        self._store.update_approval_status(request_id, ApprovalStatus.DENIED, reviewer, notes)
        self._audit.record(
            AuditEventType.APPROVAL_DENIED, reviewer,
            {"request_id": request_id, "notes": notes},
            market_id=req.market_id,
        )

        logger.info("Order denied by %s: %s", reviewer, request_id[:12])
        return True

    def pending(self) -> list[ApprovalRequest]:
        """List all pending approval requests."""
        return self._store.get_pending_approvals()

    def expire_stale(self) -> int:
        """Auto-deny expired requests. Returns count of expired requests."""
        now = time.time()
        pending = self._store.get_pending_approvals()
        expired = 0
        for req in pending:
            if now > req.expires_at:
                self._store.update_approval_status(
                    req.request_id, ApprovalStatus.EXPIRED, "system", "Auto-expired",
                )
                self._audit.record(
                    AuditEventType.APPROVAL_EXPIRED, "system",
                    {"request_id": req.request_id},
                    market_id=req.market_id,
                )
                expired += 1
        return expired

    def drain_approved(self) -> list[dict[str, Any]]:
        """Drain the approved order queue. Returns order data dicts to submit."""
        orders = list(self._approved_queue)
        self._approved_queue.clear()
        return orders
