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

* ``fetch_clients``  — WHMCS clients count (shared across all modules).
* ``fetch_accounts`` — lists SmarterMail mailboxes/mail domains via the
  Bizmail360 client.
* ``fetch_domains``  — WHMCS domains, matched against SmarterMail mail
  domains to report linked/unlinked counts.
"""
from __future__ import annotations

import logging
from typing import Any, Dict, List

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 _Bizmail360Adapter:
    module_name = "bizmail360"
    module_label = "Bizmail360"

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

            result = await whmcs.call_whmcs_safe("GetClients", {"limitnum": 1000})
            payload = (result or {}).get("clients") or {}
            clients: List[Dict[str, Any]] = 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("bizmail360.fetch_clients failed")
            return SyncAttempt(status=STATUS_FAILED, error_message=str(exc)[:500])

    async def fetch_accounts(self, db: AsyncSession) -> SyncAttempt:
        try:
            # Use the Bizmail360 domain/mailbox helpers if available.
            from services.bizmail import domains as bm_domains

            # Many SmarterMail deployments expose a "list domains" API.
            # Fall back to an explicit helper; if missing, we at least
            # count the local mirror.
            if hasattr(bm_domains, "list_domains"):
                items = await bm_domains.list_domains()  # type: ignore[attr-defined]
                count = len(items) if isinstance(items, (list, tuple)) else 0
                return SyncAttempt(
                    status=STATUS_OK,
                    items_fetched=count,
                    raw_response=_safe_preview(items),
                )

            # Fallback: count rows in the local mirror.
            from models.bizmail import BizmailMailbox  # type: ignore
            from sqlalchemy import func, select

            total = (await db.execute(select(func.count(BizmailMailbox.id)))).scalar() or 0
            return SyncAttempt(
                status=STATUS_OK,
                items_fetched=int(total),
                raw_response=f"mirrored {total} mailboxes",
            )
        except Exception as exc:  # noqa: BLE001
            logger.exception("bizmail360.fetch_accounts failed")
            return SyncAttempt(status=STATUS_FAILED, error_message=str(exc)[:500])

    async def fetch_domains(self, db: AsyncSession) -> SyncAttempt:
        try:
            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]

            # Match against any SmarterMail mail domain we can discover.
            linked = 0
            try:
                from services.bizmail import domains as bm_domains

                if hasattr(bm_domains, "list_domains"):
                    mail_domains = await bm_domains.list_domains()  # type: ignore[attr-defined]
                    mail_names = {
                        (getattr(d, "name", None) or (d.get("name") if isinstance(d, dict) else "") or "").lower()
                        for d in (mail_domains or [])
                    }
                    linked = sum(1 for d in domain_names if d in mail_names)
            except Exception:  # noqa: BLE001
                linked = 0

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


def _safe_preview(result: Any) -> str:
    try:
        import json

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


registry.register(_Bizmail360Adapter())