# @File: backend/routers/whatsapp_admin.py
# @Desc: Admin console endpoints for the WhatsApp Business Reseller Manager.
#        Mirrors bizmail_admin.py: packages CRUD, subscription list +
#        lifecycle actions, audit log, API log, integration health probe,
#        and a dashboard overview.
#
#        Master upstream credentials live in api_hub_credentials under
#        provider_id="whatsapp_server" and are never stored here.
from __future__ import annotations

import logging
import time
from datetime import datetime, timedelta, timezone
from typing import List, Optional

from core.database import get_db
from dependencies.auth import get_admin_user
from fastapi import APIRouter, Depends, HTTPException, Query, status
from models.whatsapp import (
    WhatsappAdminAuditLog,
    WhatsappApiLog,
    WhatsappClientSubscription,
    WhatsappDevice,
    WhatsappMessage,
    WhatsappPackage,
)
from schemas.auth import UserResponse
from schemas.whatsapp_admin import (
    WhatsappAdminOverview,
    WhatsappApiLogOut,
    WhatsappAuditLogOut,
    WhatsappIntegrationTestResult,
    WhatsappPackageCreate,
    WhatsappPackageOut,
    WhatsappPackageUpdate,
    WhatsappSubscriptionActionResult,
    WhatsappSubscriptionDetail,
)
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v1/admin/whatsapp", tags=["whatsapp-admin"])


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
async def _write_audit(
    db: AsyncSession,
    *,
    actor: UserResponse,
    action: str,
    target_type: Optional[str] = None,
    target_id: Optional[str] = None,
    detail: Optional[str] = None,
) -> None:
    row = WhatsappAdminAuditLog(
        actor_user_id=getattr(actor, "id", None),
        actor_email=getattr(actor, "email", None),
        action=action,
        target_type=target_type,
        target_id=target_id,
        detail=detail,
    )
    db.add(row)


async def _get_package(db: AsyncSession, package_id: int) -> Optional[WhatsappPackage]:
    return (
        await db.execute(
            select(WhatsappPackage).where(WhatsappPackage.id == package_id)
        )
    ).scalar_one_or_none()


async def _get_subscription(
    db: AsyncSession, sub_id: int
) -> Optional[WhatsappClientSubscription]:
    return (
        await db.execute(
            select(WhatsappClientSubscription).where(
                WhatsappClientSubscription.id == sub_id
            )
        )
    ).scalar_one_or_none()


async def _detail(
    db: AsyncSession, sub: WhatsappClientSubscription
) -> WhatsappSubscriptionDetail:
    detail = WhatsappSubscriptionDetail.model_validate(sub)
    pkg = await _get_package(db, sub.package_id)
    if pkg is not None:
        detail.package = WhatsappPackageOut.model_validate(pkg)
    return detail


# ---------------------------------------------------------------------------
# Packages CRUD
# ---------------------------------------------------------------------------
@router.get("/packages", response_model=List[WhatsappPackageOut])
async def admin_list_packages(
    include_inactive: bool = Query(True),
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    stmt = select(WhatsappPackage).order_by(
        WhatsappPackage.sort_order.asc(), WhatsappPackage.id.asc()
    )
    if not include_inactive:
        stmt = stmt.where(WhatsappPackage.is_active.is_(True))
    rows = (await db.execute(stmt)).scalars().all()
    return [WhatsappPackageOut.model_validate(r) for r in rows]


@router.post(
    "/packages",
    response_model=WhatsappPackageOut,
    status_code=status.HTTP_201_CREATED,
)
async def admin_create_package(
    payload: WhatsappPackageCreate,
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    existing = (
        await db.execute(
            select(WhatsappPackage).where(WhatsappPackage.slug == payload.slug)
        )
    ).scalar_one_or_none()
    if existing is not None:
        raise HTTPException(status_code=409, detail="Slug already in use.")
    row = WhatsappPackage(**payload.model_dump())
    db.add(row)
    await db.flush()
    await _write_audit(
        db,
        actor=current_user,
        action="package.create",
        target_type="package",
        target_id=str(row.id),
        detail=f"slug={row.slug}",
    )
    await db.commit()
    await db.refresh(row)
    return WhatsappPackageOut.model_validate(row)


@router.put("/packages/{package_id}", response_model=WhatsappPackageOut)
async def admin_update_package(
    package_id: int,
    payload: WhatsappPackageUpdate,
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    row = await _get_package(db, package_id)
    if row is None:
        raise HTTPException(status_code=404, detail="Package not found.")
    data = payload.model_dump(exclude_unset=True)
    # slug uniqueness check
    if "slug" in data and data["slug"] and data["slug"] != row.slug:
        clash = (
            await db.execute(
                select(WhatsappPackage).where(WhatsappPackage.slug == data["slug"])
            )
        ).scalar_one_or_none()
        if clash is not None:
            raise HTTPException(status_code=409, detail="Slug already in use.")
    for key, value in data.items():
        setattr(row, key, value)
    await _write_audit(
        db,
        actor=current_user,
        action="package.update",
        target_type="package",
        target_id=str(row.id),
        detail=",".join(sorted(data.keys())),
    )
    await db.commit()
    await db.refresh(row)
    return WhatsappPackageOut.model_validate(row)


@router.delete("/packages/{package_id}", status_code=status.HTTP_204_NO_CONTENT)
async def admin_delete_package(
    package_id: int,
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    row = await _get_package(db, package_id)
    if row is None:
        raise HTTPException(status_code=404, detail="Package not found.")
    # Soft-delete: keep historical subscriptions valid.
    row.is_active = False
    await _write_audit(
        db,
        actor=current_user,
        action="package.deactivate",
        target_type="package",
        target_id=str(row.id),
    )
    await db.commit()
    return None


# ---------------------------------------------------------------------------
# Subscriptions
# ---------------------------------------------------------------------------
@router.get("/subscriptions", response_model=List[WhatsappSubscriptionDetail])
async def admin_list_subscriptions(
    status_filter: Optional[str] = Query(None, alias="status"),
    search: Optional[str] = Query(None),
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    stmt = select(WhatsappClientSubscription).order_by(
        WhatsappClientSubscription.created_at.desc()
    )
    if status_filter:
        stmt = stmt.where(WhatsappClientSubscription.status == status_filter)
    if search:
        like = f"%{search.strip().lower()}%"
        stmt = stmt.where(
            func.lower(WhatsappClientSubscription.user_email).like(like)
        )
    rows = (await db.execute(stmt)).scalars().all()
    return [await _detail(db, sub) for sub in rows]


@router.post(
    "/subscriptions/{sub_id}/suspend",
    response_model=WhatsappSubscriptionActionResult,
)
async def admin_suspend_subscription(
    sub_id: int,
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    sub = await _get_subscription(db, sub_id)
    if sub is None:
        raise HTTPException(status_code=404, detail="Subscription not found.")
    if sub.status == "suspended":
        return WhatsappSubscriptionActionResult(
            subscription=await _detail(db, sub),
            message="Subscription already suspended.",
        )
    sub.status = "suspended"
    sub.suspended_at = datetime.now(timezone.utc)
    await _write_audit(
        db,
        actor=current_user,
        action="subscription.suspend",
        target_type="subscription",
        target_id=str(sub.id),
    )
    await db.commit()
    await db.refresh(sub)
    return WhatsappSubscriptionActionResult(
        subscription=await _detail(db, sub),
        message="Subscription suspended.",
    )


@router.post(
    "/subscriptions/{sub_id}/resume",
    response_model=WhatsappSubscriptionActionResult,
)
async def admin_resume_subscription(
    sub_id: int,
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    sub = await _get_subscription(db, sub_id)
    if sub is None:
        raise HTTPException(status_code=404, detail="Subscription not found.")
    if sub.status == "terminated":
        raise HTTPException(
            status_code=409,
            detail="A terminated subscription cannot be resumed.",
        )
    sub.status = "active"
    sub.suspended_at = None
    if sub.activated_at is None:
        sub.activated_at = datetime.now(timezone.utc)
    await _write_audit(
        db,
        actor=current_user,
        action="subscription.resume",
        target_type="subscription",
        target_id=str(sub.id),
    )
    await db.commit()
    await db.refresh(sub)
    return WhatsappSubscriptionActionResult(
        subscription=await _detail(db, sub),
        message="Subscription resumed.",
    )


@router.post(
    "/subscriptions/{sub_id}/terminate",
    response_model=WhatsappSubscriptionActionResult,
)
async def admin_terminate_subscription(
    sub_id: int,
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    sub = await _get_subscription(db, sub_id)
    if sub is None:
        raise HTTPException(status_code=404, detail="Subscription not found.")
    sub.status = "terminated"
    sub.terminated_at = datetime.now(timezone.utc)
    await _write_audit(
        db,
        actor=current_user,
        action="subscription.terminate",
        target_type="subscription",
        target_id=str(sub.id),
    )
    await db.commit()
    await db.refresh(sub)
    return WhatsappSubscriptionActionResult(
        subscription=await _detail(db, sub),
        message="Subscription terminated.",
    )


# ---------------------------------------------------------------------------
# Audit log & API log
# ---------------------------------------------------------------------------
@router.get("/audit-log", response_model=List[WhatsappAuditLogOut])
async def admin_audit_log(
    limit: int = Query(200, ge=1, le=1000),
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    stmt = (
        select(WhatsappAdminAuditLog)
        .order_by(WhatsappAdminAuditLog.created_at.desc())
        .limit(limit)
    )
    rows = (await db.execute(stmt)).scalars().all()
    return [WhatsappAuditLogOut.model_validate(r) for r in rows]


@router.get("/api-log", response_model=List[WhatsappApiLogOut])
async def admin_api_log(
    limit: int = Query(200, ge=1, le=1000),
    subscription_id: Optional[int] = Query(None),
    channel: Optional[str] = Query(None),
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    stmt = select(WhatsappApiLog).order_by(WhatsappApiLog.created_at.desc())
    if subscription_id is not None:
        stmt = stmt.where(WhatsappApiLog.subscription_id == subscription_id)
    if channel:
        stmt = stmt.where(WhatsappApiLog.channel == channel)
    stmt = stmt.limit(limit)
    rows = (await db.execute(stmt)).scalars().all()
    return [WhatsappApiLogOut.model_validate(r) for r in rows]


# ---------------------------------------------------------------------------
# Integration health + overview
# ---------------------------------------------------------------------------
async def _probe_upstream() -> WhatsappIntegrationTestResult:
    """Best-effort ping of the upstream WhatsApp server.

    We resolve credentials from the API Hub under provider_id=whatsapp_server.
    If the credentials aren't configured yet, return a friendly message
    instead of raising, so the admin home still loads.
    """
    base_url: Optional[str] = None
    try:
        # Local import to avoid circular imports during boot.
        from services.api_hub.credentials import get_credentials  # type: ignore

        creds = await get_credentials("whatsapp_server")
        if creds:
            base_url = creds.get("base_url") or creds.get("server_url")
    except Exception:  # noqa: BLE001
        creds = None  # fall through

    if not base_url:
        return WhatsappIntegrationTestResult(
            ok=False,
            message=(
                "No upstream WhatsApp server configured. Add credentials in "
                "API Hub → Integrations → provider 'whatsapp_server'."
            ),
        )

    try:
        import httpx  # local import to keep startup fast

        t0 = time.perf_counter()
        async with httpx.AsyncClient(timeout=8.0) as http:
            resp = await http.get(base_url.rstrip("/") + "/")
        latency = int((time.perf_counter() - t0) * 1000)
        ok = resp.status_code < 500
        return WhatsappIntegrationTestResult(
            ok=ok,
            message=(
                f"Upstream responded with HTTP {resp.status_code}."
                if ok
                else f"Upstream returned HTTP {resp.status_code}."
            ),
            latency_ms=latency,
            upstream_base_url=base_url,
        )
    except Exception as exc:  # noqa: BLE001
        return WhatsappIntegrationTestResult(
            ok=False,
            message=f"Upstream unreachable: {exc}",
            upstream_base_url=base_url,
        )


@router.post("/integration/test", response_model=WhatsappIntegrationTestResult)
async def admin_test_integration(
    current_user: UserResponse = Depends(get_admin_user),
):
    return await _probe_upstream()


@router.get("/overview", response_model=WhatsappAdminOverview)
async def admin_overview(
    current_user: UserResponse = Depends(get_admin_user),
    db: AsyncSession = Depends(get_db),
):
    async def _count(stmt) -> int:
        result = await db.execute(stmt)
        return int(result.scalar_one() or 0)

    total_packages = await _count(select(func.count(WhatsappPackage.id)))
    active_packages = await _count(
        select(func.count(WhatsappPackage.id)).where(
            WhatsappPackage.is_active.is_(True)
        )
    )

    total_subs = await _count(select(func.count(WhatsappClientSubscription.id)))
    active_subs = await _count(
        select(func.count(WhatsappClientSubscription.id)).where(
            WhatsappClientSubscription.status == "active"
        )
    )
    suspended_subs = await _count(
        select(func.count(WhatsappClientSubscription.id)).where(
            WhatsappClientSubscription.status == "suspended"
        )
    )
    terminated_subs = await _count(
        select(func.count(WhatsappClientSubscription.id)).where(
            WhatsappClientSubscription.status == "terminated"
        )
    )

    total_devices = await _count(select(func.count(WhatsappDevice.id)))
    connected_devices = await _count(
        select(func.count(WhatsappDevice.id)).where(
            WhatsappDevice.status == "connected"
        )
    )

    since = datetime.now(timezone.utc) - timedelta(hours=24)
    messages_last_24h = await _count(
        select(func.count(WhatsappMessage.id)).where(
            WhatsappMessage.created_at >= since
        )
    )

    audit_rows = (
        (
            await db.execute(
                select(WhatsappAdminAuditLog)
                .order_by(WhatsappAdminAuditLog.created_at.desc())
                .limit(10)
            )
        )
        .scalars()
        .all()
    )

    return WhatsappAdminOverview(
        total_packages=total_packages,
        active_packages=active_packages,
        total_subscriptions=total_subs,
        active_subscriptions=active_subs,
        suspended_subscriptions=suspended_subs,
        terminated_subscriptions=terminated_subs,
        total_devices=total_devices,
        connected_devices=connected_devices,
        messages_last_24h=messages_last_24h,
        recent_audit=[WhatsappAuditLogOut.model_validate(r) for r in audit_rows],
    )