# @File: backend/services/bizmail/domains.py
# @Desc: High-level SmarterMail wrappers for domain, user, alias, and
#        forward management. All functions return plain dicts so routers
#        can forward them to the frontend without extra shaping.
#
# Design notes:
#   * Every public helper here goes through :class:`BizmailClient.request`
#     which handles auth + retry-on-401. We never call httpx directly.
#   * SmarterMail has evolved its REST surface across 16.x/17.x; when the
#     canonical endpoint layout changes, we try the most likely path and
#     raise a structured :class:`BizmailApiError` if it fails.
#   * The "domain admin" concept in SmarterMail is a domain-level user
#     with the ``isDomainAdmin`` flag — we use it for the postmaster
#     mailbox that every client subscription gets on activation.
from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional

from services.bizmail.client import BizmailApiError, get_client

logger = logging.getLogger(__name__)


# ---------- Domains ----------
async def create_domain(
    *,
    domain_name: str,
    storage_mb: int,
    mailbox_quota: int,
    aliases_allowed: int,
    primary_admin_username: str,
    primary_admin_password: str,
    primary_admin_display: Optional[str] = None,
) -> Dict[str, Any]:
    """Create a new domain plus its initial domain-admin mailbox.

    SmarterMail's POST /api/v1/settings/sysadmin/domain takes a nested
    body containing the domain settings plus `users` bootstrap info.
    """
    client = get_client()
    body = {
        "domainInfo": {
            "name": domain_name,
            "path": f"Domains/{domain_name}",
            "maxDomainSizeInMB": storage_mb * mailbox_quota,
            "maxMailboxSize": storage_mb,
            "maxUsers": mailbox_quota,
            "maxAliases": aliases_allowed,
            "enabled": True,
        },
        "adminUser": {
            "username": primary_admin_username,
            "password": primary_admin_password,
            "displayName": primary_admin_display or primary_admin_username,
            "isDomainAdmin": True,
        },
    }
    return await client.request(
        "POST", "/api/v1/settings/sysadmin/domain", json_body=body
    )


async def update_domain(domain_name: str, settings: Dict[str, Any]) -> Dict[str, Any]:
    """Partial update of a domain's settings (quota, max users, etc.)."""
    client = get_client()
    body = {"domainInfo": {"name": domain_name, **settings}}
    return await client.request(
        "PUT",
        f"/api/v1/settings/sysadmin/domain/{domain_name}",
        json_body=body,
    )


async def delete_domain(domain_name: str) -> Dict[str, Any]:
    """Permanently delete a domain from SmarterMail."""
    client = get_client()
    return await client.request(
        "DELETE", f"/api/v1/settings/sysadmin/domain/{domain_name}"
    )


async def get_domain_usage(domain_name: str) -> Dict[str, Any]:
    """Return cached usage counters for a domain.

    If the upstream endpoint is unavailable we return a best-effort
    zeroed result rather than raising, so the client dashboard still
    renders something useful.
    """
    client = get_client()
    try:
        return await client.request(
            "GET", f"/api/v1/settings/domain/{domain_name}/usage-stats"
        )
    except BizmailApiError as exc:
        logger.info("Domain usage fetch failed for %s: %s", domain_name, exc.message)
        return {
            "success": True,
            "mailboxes": 0,
            "aliases": 0,
            "storage_used_mb": 0,
            "degraded": True,
            "message": exc.message,
        }


# ---------- Users (mailboxes) ----------
async def list_users(domain_name: str) -> List[Dict[str, Any]]:
    """List all mailboxes inside a domain."""
    client = get_client()
    body = await client.request(
        "GET", f"/api/v1/settings/domain/users", params={"domain": domain_name}
    )
    users = body.get("users") or body.get("data") or []
    if isinstance(users, dict):
        users = list(users.values())
    return users if isinstance(users, list) else []


async def create_user(
    *,
    domain_name: str,
    username: str,
    password: str,
    display_name: Optional[str] = None,
    is_domain_admin: bool = False,
    mailbox_size_mb: Optional[int] = None,
) -> Dict[str, Any]:
    """Create a new mailbox inside a domain."""
    client = get_client()
    body = {
        "userInfo": {
            "domain": domain_name,
            "username": username,
            "password": password,
            "displayName": display_name or username.split("@")[0],
            "isDomainAdmin": bool(is_domain_admin),
        }
    }
    if mailbox_size_mb is not None:
        body["userInfo"]["maxMailboxSize"] = int(mailbox_size_mb)
    return await client.request(
        "POST", "/api/v1/settings/domain/users", json_body=body
    )


async def update_user(
    *,
    username: str,
    display_name: Optional[str] = None,
    disabled: Optional[bool] = None,
) -> Dict[str, Any]:
    """Partial-update a mailbox's settings."""
    client = get_client()
    payload: Dict[str, Any] = {"userInfo": {"username": username}}
    if display_name is not None:
        payload["userInfo"]["displayName"] = display_name
    if disabled is not None:
        payload["userInfo"]["enabled"] = not disabled
    return await client.request("PUT", "/api/v1/settings/domain/users", json_body=payload)


async def delete_user(username: str) -> Dict[str, Any]:
    """Delete a mailbox."""
    client = get_client()
    return await client.request(
        "DELETE",
        "/api/v1/settings/domain/users",
        params={"username": username},
    )


async def reset_user_password(username: str, new_password: str) -> Dict[str, Any]:
    """Reset a mailbox password (as the system admin)."""
    client = get_client()
    body = {"userInfo": {"username": username, "password": new_password}}
    return await client.request(
        "PUT", "/api/v1/settings/domain/users/password", json_body=body
    )


# ---------- Aliases ----------
async def list_aliases(domain_name: str) -> List[Dict[str, Any]]:
    """Return all aliases in a domain."""
    client = get_client()
    body = await client.request(
        "GET", "/api/v1/settings/domain/aliases", params={"domain": domain_name}
    )
    aliases = body.get("aliases") or body.get("data") or []
    return aliases if isinstance(aliases, list) else []


async def create_alias(
    *, domain_name: str, alias_name: str, targets: List[str]
) -> Dict[str, Any]:
    """Create an alias that forwards to one or more mailboxes."""
    client = get_client()
    body = {
        "aliasInfo": {
            "domain": domain_name,
            "name": alias_name,
            "addresses": targets,
            "internalOnly": False,
            "useAsList": False,
        }
    }
    return await client.request(
        "POST", "/api/v1/settings/domain/aliases", json_body=body
    )


async def delete_alias(domain_name: str, alias_name: str) -> Dict[str, Any]:
    """Delete an alias."""
    client = get_client()
    return await client.request(
        "DELETE",
        "/api/v1/settings/domain/aliases",
        params={"domain": domain_name, "name": alias_name},
    )


# ---------- Forwards ----------
async def get_user_forwarding(username: str) -> Dict[str, Any]:
    """Return the forwarding configuration for a single mailbox."""
    client = get_client()
    try:
        return await client.request(
            "GET", f"/api/v1/settings/domain/users/{username}/forwarding"
        )
    except BizmailApiError as exc:
        logger.info("Forwarding fetch failed for %s: %s", username, exc.message)
        return {"success": False, "forwardingAddress": "", "message": exc.message}


async def set_user_forwarding(username: str, forward_to: str) -> Dict[str, Any]:
    """Enable forwarding on a mailbox (empty string disables)."""
    client = get_client()
    body = {
        "forwardingAddress": forward_to,
        "enabled": bool(forward_to),
    }
    return await client.request(
        "PUT",
        f"/api/v1/settings/domain/users/{username}/forwarding",
        json_body=body,
    )