"""UptimeRobot adapter for the Fetch Data Toggle system.

* ``fetch_clients``  — pulls WHMCS clients and returns the count.
* ``fetch_accounts`` — runs the existing ``services.uptimerobot.sync.run_sync``
  which mirrors the UptimeRobot monitors tables.
* ``fetch_domains``  — pulls WHMCS domains, tries to match each domain
  with a known monitor (by substring of the monitor URL/friendly name),
  and reports linked vs. unlinked counts.

All upstream errors are captured into :class:`SyncAttempt` with
``status="failed"`` so the engine can persist a row and the UI can
render a red error chip.
"""
from __future__ import annotations

import logging
from typing import Any, Dict, List

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from services.module_sync.adapters import registry
from services.module_sync.engine import STATUS_FAILED, STATUS_OK, SyncAttempt

logger = logging.getLogger(__name__)


class _UptimeRobotAdapter:
    module_name = "uptimerobot"
    module_label = "UptimeRobot"

    async def fetch_clients(self, db: AsyncSession) -> SyncAttempt:
        try:
            from services import whmcs

            result = await whmcs.call_whmcs_safe("GetClients", {"limitnum": 1000})
            if not result or not isinstance(result, dict):
                return SyncAttempt(
                    status=STATUS_FAILED,
                    error_message="WHMCS returned an empty response",
                )
            payload = result.get("clients") or result
            clients: List[Dict[str, Any]] = []
            if isinstance(payload, dict):
                clients = list(payload.get("client", []) or [])
            return SyncAttempt(
                status=STATUS_OK,
                items_fetched=len(clients),
                raw_response=_safe_preview(result),
            )
        except Exception as exc:  # noqa: BLE001
            logger.exception("uptimerobot.fetch_clients failed")
            return SyncAttempt(status=STATUS_FAILED, error_message=str(exc)[:500])

    async def fetch_accounts(self, db: AsyncSession) -> SyncAttempt:
        try:
            from models.uptimerobot import UptimeClientMonitor
            from services.uptimerobot.sync import run_sync as ur_run_sync

            await ur_run_sync(db)

            total = (
                await db.execute(select(UptimeClientMonitor.id))
            ).scalars().all()
            return SyncAttempt(
                status=STATUS_OK,
                items_fetched=len(total),
                raw_response=f"mirrored {len(total)} monitors",
            )
        except Exception as exc:  # noqa: BLE001
            logger.exception("uptimerobot.fetch_accounts failed")
            return SyncAttempt(status=STATUS_FAILED, error_message=str(exc)[:500])

    async def fetch_domains(self, db: AsyncSession) -> SyncAttempt:
        try:
            from models.uptimerobot import UptimeClientMonitor
            from services import whmcs

            result = await whmcs.call_whmcs_safe("GetClientsDomains", {"limitnum": 1000})
            domains_block = (result or {}).get("domains") or {}
            whmcs_domains = list(domains_block.get("domain", []) or [])
            domain_names = [
                (d.get("domainname") or "").strip().lower()
                for d in whmcs_domains
                if isinstance(d, dict)
            ]
            domain_names = [d for d in domain_names if d]

            monitors = (
                await db.execute(
                    select(UptimeClientMonitor.url, UptimeClientMonitor.friendly_name)
                )
            ).all()
            haystack = " | ".join(
                (
                    (url or "").lower() + " " + (fn or "").lower()
                    for url, fn in monitors
                )
            )

            linked = sum(1 for d in domain_names if d and d in haystack)
            unlinked = len(domain_names) - linked
            return SyncAttempt(
                status=STATUS_OK,
                items_fetched=len(domain_names),
                raw_response=_safe_preview(result),
                extra={"linked": linked, "unlinked": max(unlinked, 0)},
            )
        except Exception as exc:  # noqa: BLE001
            logger.exception("uptimerobot.fetch_domains failed")
            return SyncAttempt(status=STATUS_FAILED, error_message=str(exc)[:500])


def _safe_preview(result: Any) -> str:
    """Trim WHMCS responses so the masked raw_response stays readable."""
    try:
        import json

        text = json.dumps(result, default=str)
        return text[:4000]
    except Exception:  # noqa: BLE001
        return str(result)[:4000]


registry.register(_UptimeRobotAdapter())