# @File: backend/routers/admin_dashboard.py
# @Desc: Live admin dashboard data aggregator. Pulls real numbers from WHMCS
#        (clients, orders, tickets, invoices, products) and the local DB
#        (notifications, support_seats, plans). No mock data.
import logging
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional

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

from core.database import get_db
from dependencies.auth import get_current_user
from schemas.auth import UserResponse
from services.whmcs import call_whmcs_safe

logger = logging.getLogger(__name__)

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


def _require_admin(user: UserResponse) -> None:
    is_admin = bool(
        getattr(user, "is_admin", False)
        or getattr(user, "is_superuser", False)
        or getattr(user, "role", "") in ("admin", "superadmin")
    )
    if not is_admin:
        raise HTTPException(status_code=403, detail="Admin access required.")


def _safe_float(v: Any, default: float = 0.0) -> float:
    try:
        return float(v)
    except (TypeError, ValueError):
        return default


def _safe_int(v: Any, default: int = 0) -> int:
    try:
        return int(v)
    except (TypeError, ValueError):
        return default


def _as_list(value: Any) -> List[Dict[str, Any]]:
    if value is None:
        return []
    if isinstance(value, list):
        return [x for x in value if isinstance(x, dict)]
    if isinstance(value, dict):
        return [value]
    return []


class KpiCard(BaseModel):
    label: str
    value: str
    raw: float = 0
    trend: Optional[str] = None


class RecentOrder(BaseModel):
    id: str
    client: str
    product: str
    total: str
    status: str


class TicketQueueItem(BaseModel):
    id: str
    subject: str
    dept: str
    priority: str
    agent: Optional[str] = None


class TopProduct(BaseModel):
    name: str
    count: int
    revenue: str
    pct: int


class RevenuePoint(BaseModel):
    label: str
    value: float


class DashboardPayload(BaseModel):
    kpis: List[KpiCard]
    revenue_series: List[RevenuePoint]
    top_products: List[TopProduct]
    recent_orders: List[RecentOrder]
    ticket_queue: List[TicketQueueItem]
    whmcs_ok: bool
    errors: List[str] = []


@router.get("/dashboard", response_model=DashboardPayload)
async def admin_dashboard(
    current_user: UserResponse = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    _require_admin(current_user)

    errors: List[str] = []
    whmcs_ok = True

    # NOTE: call_whmcs_safe() returns the WHMCS JSON body on success (dict) or
    #       None on error (never raises). The user-email tagging for logs is
    #       handled automatically via the _current_user_email ContextVar set by
    #       the auth dependency — do NOT pass user_email as a kwarg here.

    def _whmcs_failed(label: str) -> None:
        nonlocal whmcs_ok
        whmcs_ok = False
        errors.append(f"{label}: upstream error")

    # ---- WHMCS: clients ----
    clients_count = 0
    res = await call_whmcs_safe("GetClients", {"limitnum": 1})
    if isinstance(res, dict):
        clients_count = _safe_int(res.get("totalresults"))
    else:
        _whmcs_failed("GetClients")

    # ---- WHMCS: orders (last 30 days) ----
    new_orders_30d = 0
    recent_orders: List[RecentOrder] = []
    since = (datetime.utcnow() - timedelta(days=30)).strftime("%Y-%m-%d")
    res = await call_whmcs_safe(
        "GetOrders", {"limitnum": 10, "status": "Active"}
    )
    if isinstance(res, dict):
        orders_block = res.get("orders") or {}
        order_rows = (
            _as_list(orders_block.get("order"))
            if isinstance(orders_block, dict)
            else []
        )
        for o in order_rows[:5]:
            lineitems = o.get("lineitems")
            product_name = "—"
            if isinstance(lineitems, dict):
                items = _as_list(lineitems.get("lineitem"))
                if items:
                    product_name = str(items[0].get("product") or "—")
            recent_orders.append(
                RecentOrder(
                    id=str(o.get("ordernum") or o.get("id") or ""),
                    client=(
                        str(o.get("name") or f"{o.get('firstname', '')} {o.get('lastname', '')}").strip()
                        or f"Client #{o.get('userid', '')}"
                    ),
                    product=product_name,
                    total=f"${_safe_float(o.get('amount')):.2f}",
                    status=str(o.get("status") or "Pending"),
                )
            )
        # Count last 30d by filtering date
        for o in order_rows:
            d = str(o.get("date") or "")
            if d and d[:10] >= since:
                new_orders_30d += 1
    else:
        _whmcs_failed("GetOrders")

    # ---- WHMCS: tickets ----
    open_tickets = 0
    ticket_queue: List[TicketQueueItem] = []
    res = await call_whmcs_safe(
        "GetTickets", {"limitnum": 10, "status": "Open"}
    )
    if isinstance(res, dict):
        open_tickets = _safe_int(res.get("totalresults"))
        tblock = res.get("tickets") or {}
        trows = _as_list(tblock.get("ticket")) if isinstance(tblock, dict) else []
        for t in trows[:5]:
            ticket_queue.append(
                TicketQueueItem(
                    id=f"#{t.get('tid') or t.get('id') or ''}",
                    subject=str(t.get("subject") or "(no subject)"),
                    dept=str(t.get("deptname") or "General"),
                    priority=str(t.get("priority") or "Medium"),
                    agent=str(t.get("admin") or "") or None,
                )
            )
    else:
        _whmcs_failed("GetTickets")

    # ---- WHMCS: revenue / MRR from invoices (paid last 12 months) ----
    mrr = 0.0
    revenue_series: List[RevenuePoint] = []
    top_products: List[TopProduct] = []
    res = await call_whmcs_safe(
        "GetInvoices", {"limitnum": 500, "status": "Paid"}
    )
    if isinstance(res, dict):
        iblock = res.get("invoices") or {}
        irows = _as_list(iblock.get("invoice")) if isinstance(iblock, dict) else []
        now = datetime.utcnow()
        buckets: Dict[str, float] = {}
        for i in range(11, -1, -1):
            d = (now.replace(day=1) - timedelta(days=30 * i)).strftime("%Y-%m")
            buckets[d] = 0.0
        this_month_key = now.strftime("%Y-%m")
        for inv in irows:
            amt = _safe_float(inv.get("total"))
            datepaid = str(inv.get("datepaid") or inv.get("date") or "")[:7]
            if datepaid in buckets:
                buckets[datepaid] += amt
            if datepaid == this_month_key:
                mrr += amt
        for k, v in buckets.items():
            revenue_series.append(RevenuePoint(label=k, value=round(v, 2)))
    else:
        _whmcs_failed("GetInvoices")

    # ---- WHMCS: top products ----
    res = await call_whmcs_safe("GetProducts", {})
    if isinstance(res, dict):
        pblock = res.get("products") or {}
        prows = _as_list(pblock.get("product")) if isinstance(pblock, dict) else []
        # Sort by paytype or id for now; WHMCS doesn't return sales count in GetProducts.
        # We use pricing as proxy for revenue rank.
        def _prod_price(p: Dict[str, Any]) -> float:
            pricing = p.get("pricing") or {}
            if isinstance(pricing, dict):
                for cur in pricing.values():
                    if isinstance(cur, dict):
                        monthly = _safe_float(cur.get("monthly"))
                        if monthly > 0:
                            return monthly
            return 0.0

        ranked = sorted(prows, key=_prod_price, reverse=True)[:4]
        if ranked:
            max_price = _prod_price(ranked[0]) or 1.0
            for p in ranked:
                price = _prod_price(p)
                top_products.append(
                    TopProduct(
                        name=str(p.get("name") or "—"),
                        count=0,
                        revenue=f"${price:,.2f}/mo",
                        pct=int(min(100, (price / max_price) * 100)) if max_price else 0,
                    )
                )
    else:
        # non-fatal
        errors.append("GetProducts: upstream error")

    kpis = [
        KpiCard(label="MRR", value=f"${mrr:,.0f}", raw=mrr),
        KpiCard(label="Active Clients", value=f"{clients_count:,}", raw=clients_count),
        KpiCard(label="New Orders (30d)", value=f"{new_orders_30d:,}", raw=new_orders_30d),
        KpiCard(label="Open Tickets", value=f"{open_tickets:,}", raw=open_tickets),
    ]

    return DashboardPayload(
        kpis=kpis,
        revenue_series=revenue_series,
        top_products=top_products,
        recent_orders=recent_orders,
        ticket_queue=ticket_queue,
        whmcs_ok=whmcs_ok,
        errors=errors,
    )


# ---- Search ----
class SearchResultItem(BaseModel):
    type: str  # "client" | "order" | "ticket"
    id: str
    title: str
    subtitle: Optional[str] = None
    link: str


class SearchResponse(BaseModel):
    items: List[SearchResultItem]


@router.get("/search", response_model=SearchResponse)
async def admin_search(
    q: str = Query(..., min_length=1),
    current_user: UserResponse = Depends(get_current_user),
):
    _require_admin(current_user)
    items: List[SearchResultItem] = []

    # Clients
    res = await call_whmcs_safe("GetClients", {"search": q, "limitnum": 5})
    if isinstance(res, dict):
        cblock = res.get("clients") or {}
        crows = _as_list(cblock.get("client")) if isinstance(cblock, dict) else []
        for c in crows:
            name = (
                f"{c.get('firstname','')} {c.get('lastname','')}".strip()
                or c.get("companyname")
                or c.get("email")
                or "—"
            )
            items.append(
                SearchResultItem(
                    type="client",
                    id=str(c.get("id") or ""),
                    title=name,
                    subtitle=str(c.get("email") or ""),
                    link=f"/admin/clients/{c.get('id')}",
                )
            )

    # Orders (by id)
    if q.replace("ORD-", "").isdigit():
        onum = q.replace("ORD-", "")
        res = await call_whmcs_safe("GetOrders", {"id": int(onum)})
        if isinstance(res, dict):
            oblock = res.get("orders") or {}
            orows = _as_list(oblock.get("order")) if isinstance(oblock, dict) else []
            for o in orows[:5]:
                items.append(
                    SearchResultItem(
                        type="order",
                        id=str(o.get("id") or ""),
                        title=f"Order #{o.get('ordernum') or o.get('id')}",
                        subtitle=f"${_safe_float(o.get('amount')):.2f} · {o.get('status','')}",
                        link=f"/admin/orders/{o.get('id')}",
                    )
                )

    return SearchResponse(items=items)