"""
Admin monitoring endpoints for enrichment operations.

Provides real-time visibility into:
- Active reservations (credit holds in progress)
- Outbox queue backlog (retryable ledger writes)
- Daily statistics and spend tracking
- PDL API health status
"""

from __future__ import annotations

import logging
from datetime import datetime, timedelta, timezone
from decimal import Decimal
from typing import Any, Dict, List

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from ....auth.admin_auth import require_admin, AdminContext
from ....db import get_session
from ....models import (
    Account,
    EnrichmentLedgerOutbox,
    EnrichmentReservation,
    EnrichmentReservationStatus,
    EnrichmentUsage,
)

logger = logging.getLogger(__name__)

router = APIRouter()


def _now_utc() -> datetime:
    return datetime.now(timezone.utc)


# ====================================================================
# Monitor Stats
# ====================================================================


@router.get("/stats")
async def get_monitor_stats(
    _admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """
    Return high-level system statistics for the admin monitor dashboard.
    """
    now = _now_utc()
    day_start = now.replace(hour=0, minute=0, second=0, microsecond=0)

    # Active reservations count
    active_reservations_stmt = (
        select(func.count())
        .select_from(EnrichmentReservation)
        .where(EnrichmentReservation.status == EnrichmentReservationStatus.RESERVED)
    )
    active_reservations_result = await db.execute(active_reservations_stmt)
    active_reservations = active_reservations_result.scalar() or 0

    # Outbox backlog count
    outbox_stmt = (
        select(func.count())
        .select_from(EnrichmentLedgerOutbox)
        .where(EnrichmentLedgerOutbox.status == "pending")
    )
    outbox_result = await db.execute(outbox_stmt)
    outbox_backlog = outbox_result.scalar() or 0

    # Today's stats
    today_stats_stmt = select(
        func.count(EnrichmentUsage.id),
        func.coalesce(func.sum(EnrichmentUsage.matches_found), 0),
        func.coalesce(func.sum(EnrichmentUsage.cost_usd), Decimal("0")),
    ).where(EnrichmentUsage.created_at >= day_start)
    today_result = await db.execute(today_stats_stmt)
    today_row = today_result.first()

    today_total = int(today_row[0] or 0)
    today_success = int(today_row[1] or 0)
    today_spend_usd = float(today_row[2] or Decimal("0"))
    today_failed = max(0, today_total - today_success)

    # Sweeper last run (look for most recent expired reservation)
    sweeper_stmt = (
        select(EnrichmentReservation.finalized_at)
        .where(EnrichmentReservation.status == EnrichmentReservationStatus.EXPIRED)
        .order_by(EnrichmentReservation.finalized_at.desc())
        .limit(1)
    )
    sweeper_result = await db.execute(sweeper_stmt)
    sweeper_last_run = sweeper_result.scalar()

    # PDL API health (simplified - check if we have recent successful operations)
    recent_cutoff = now - timedelta(minutes=15)
    health_stmt = (
        select(func.count())
        .select_from(EnrichmentUsage)
        .where(
            EnrichmentUsage.created_at >= recent_cutoff,
            EnrichmentUsage.error.is_(None),
        )
    )
    health_result = await db.execute(health_stmt)
    recent_success_count = health_result.scalar() or 0
    pdl_api_healthy = (
        recent_success_count > 0
    )  # If we've had any success in last 15m, assume healthy

    return {
        "active_reservations": active_reservations,
        "outbox_backlog": outbox_backlog,
        "today_total": today_total,
        "today_success": today_success,
        "today_failed": today_failed,
        "today_spend_usd": today_spend_usd,
        "sweeper_last_run": sweeper_last_run.isoformat() if sweeper_last_run else None,
        "pdl_api_healthy": pdl_api_healthy,
    }


# ====================================================================
# Active Reservations
# ====================================================================


@router.get("/reservations")
async def get_active_reservations(
    limit: int = 50,
    _admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, List[Dict[str, Any]]]:
    """
    Return currently active credit reservations.
    """
    stmt = (
        select(EnrichmentReservation, Account.name)
        .join(Account, EnrichmentReservation.account_id == Account.id)
        .where(EnrichmentReservation.status == EnrichmentReservationStatus.RESERVED)
        .order_by(EnrichmentReservation.created_at.desc())
        .limit(limit)
    )

    result = await db.execute(stmt)
    rows = result.all()

    reservations = []
    for reservation, account_name in rows:
        reservations.append(
            {
                "reservation_id": str(reservation.id),
                "account_id": reservation.account_id,
                "account_name": account_name or "Unknown",
                "job_id": reservation.job_id or "",
                "chunk_id": reservation.chunk_id or "",
                "credits_reserved": reservation.required_credits,
                "expires_at": reservation.lease_expires_at.isoformat()
                if reservation.lease_expires_at
                else None,
                "heartbeat_at": reservation.last_heartbeat_at.isoformat()
                if reservation.last_heartbeat_at
                else None,
            }
        )

    return {"reservations": reservations}


@router.post("/reservations/{reservation_id}/cancel")
async def cancel_reservation(
    reservation_id: int,
    _admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """
    Manually cancel/release a reservation and free up credits.
    """
    async with db.begin():
        stmt = (
            select(EnrichmentReservation)
            .where(EnrichmentReservation.id == reservation_id)
            .with_for_update()
        )
        result = await db.execute(stmt)
        reservation = result.scalar_one_or_none()

        if not reservation:
            raise HTTPException(status_code=404, detail="Reservation not found")

        if reservation.status != EnrichmentReservationStatus.RESERVED:
            raise HTTPException(
                status_code=400,
                detail=f"Cannot cancel reservation with status: {reservation.status}",
            )

        reservation.status = EnrichmentReservationStatus.RELEASED
        reservation.finalized_at = _now_utc()
        reservation.lease_expires_at = None
        reservation.last_heartbeat_at = None

    logger.info(
        "Admin cancelled reservation %s (account=%s, credits=%s)",
        reservation_id,
        reservation.account_id,
        reservation.required_credits,
    )

    return {
        "success": True,
        "reservation_id": reservation_id,
        "credits_released": reservation.required_credits,
    }


# ====================================================================
# Outbox Queue
# ====================================================================


@router.get("/outbox")
async def get_outbox_items(
    limit: int = 50,
    _admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, List[Dict[str, Any]]]:
    """
    Return pending items in the enrichment ledger outbox.
    """
    stmt = (
        select(EnrichmentLedgerOutbox, Account.name)
        .join(Account, EnrichmentLedgerOutbox.account_id == Account.id)
        .where(EnrichmentLedgerOutbox.status == "pending")
        .order_by(EnrichmentLedgerOutbox.created_at.asc())
        .limit(limit)
    )

    result = await db.execute(stmt)
    rows = result.all()

    items = []
    for outbox, account_name in rows:
        items.append(
            {
                "id": outbox.id,
                "reservation_id": str(outbox.reservation_id),
                "account_id": outbox.account_id,
                "account_name": account_name or "Unknown",
                "operation": outbox.operation,
                "attempts": outbox.attempt_count,
                "next_retry_at": outbox.next_attempt_at.isoformat()
                if outbox.next_attempt_at
                else None,
                "created_at": outbox.created_at.isoformat()
                if outbox.created_at
                else None,
            }
        )

    return {"items": items}


@router.post("/outbox/{item_id}/retry")
async def retry_outbox_item(
    item_id: int,
    _admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
) -> Dict[str, Any]:
    """
    Manually trigger immediate retry for a stuck outbox item.
    """
    async with db.begin():
        stmt = (
            select(EnrichmentLedgerOutbox)
            .where(EnrichmentLedgerOutbox.id == item_id)
            .with_for_update()
        )
        result = await db.execute(stmt)
        outbox = result.scalar_one_or_none()

        if not outbox:
            raise HTTPException(status_code=404, detail="Outbox item not found")

        if outbox.status != "pending":
            raise HTTPException(
                status_code=400,
                detail=f"Cannot retry item with status: {outbox.status}",
            )

        # Reset next attempt to now to trigger immediate retry
        outbox.next_attempt_at = _now_utc()

    logger.info(
        "Admin triggered retry for outbox item %s (account=%s, attempts=%s)",
        item_id,
        outbox.account_id,
        outbox.attempt_count,
    )

    return {
        "success": True,
        "item_id": item_id,
        "next_retry_at": outbox.next_attempt_at.isoformat(),
    }
