# @File: backend/services/password_reset.py
# @Desc: Password reset helpers. We mint a short-lived signed JWT using the app's
#        JWT secret (the same mechanism used by Atoms Cloud auth for access tokens),
#        and send the reset email to the WHMCS-linked client through WHMCS SendEmail
#        API so the email goes out via WHMCS's own configured SMTP.
import hashlib
import logging
import os
from typing import Any, Dict, Optional

from fastapi import HTTPException

from core.auth import AccessTokenError, create_access_token, decode_access_token
from services.whmcs import call_whmcs

logger = logging.getLogger(__name__)

# Password reset tokens are intentionally short-lived.
RESET_TOKEN_EXPIRE_MINUTES = 30
RESET_TOKEN_PURPOSE = "password_reset"


def _hash_email(email: str) -> str:
    """Return a short stable hash for log-safe identification."""
    return hashlib.sha256(email.strip().lower().encode()).hexdigest()[:10]


def create_reset_token(email: str, client_id: Optional[int]) -> str:
    """Create a signed JWT reset token scoped to password reset only.

    The token includes a `purpose` claim so it cannot be mistaken for an
    access token, and is signed with the app JWT secret via core.auth.
    """
    claims: Dict[str, Any] = {
        "sub": email.strip().lower(),
        "purpose": RESET_TOKEN_PURPOSE,
        "whmcs_client_id": int(client_id) if client_id else 0,
    }
    return create_access_token(claims, expires_minutes=RESET_TOKEN_EXPIRE_MINUTES)


def verify_reset_token(token: str) -> Dict[str, Any]:
    """Verify a reset token and return its payload.

    Raises HTTPException(400) on invalid / expired / wrong-purpose tokens.
    """
    try:
        payload = decode_access_token(token)
    except AccessTokenError as exc:
        raise HTTPException(
            status_code=400, detail="This password reset link is invalid or has expired."
        ) from exc

    if payload.get("purpose") != RESET_TOKEN_PURPOSE:
        raise HTTPException(
            status_code=400, detail="This password reset link is not valid."
        )

    email = str(payload.get("sub") or "").strip().lower()
    if not email:
        raise HTTPException(
            status_code=400, detail="This password reset link is not valid."
        )
    return payload


def _portal_base_url() -> str:
    """Resolve the public portal URL used to build the reset link."""
    for key in ("PORTAL_BASE_URL", "FRONTEND_BASE_URL", "APP_BASE_URL"):
        value = os.environ.get(key)
        if value:
            return value.rstrip("/")
    # Reasonable default; admins should set PORTAL_BASE_URL in production.
    return "https://client.doiteasy.net"


def build_reset_link(token: str) -> str:
    return f"{_portal_base_url()}/reset-password?token={token}"


async def send_reset_email_via_whmcs(client_id: int, reset_link: str) -> bool:
    """Send the password reset email through WHMCS so it uses WHMCS's SMTP.

    Uses the WHMCS SendEmail API with customtype=general, which lets us deliver
    arbitrary subject + HTML body to a specific client by WHMCS client id.
    Returns True if WHMCS accepted the request, False otherwise.
    """
    subject = "Reset your DoItEasy account password"
    html_body = (
        "<p>Hello,</p>"
        "<p>We received a request to reset the password on your DoItEasy account. "
        "Click the link below to choose a new password. This link will expire in "
        f"{RESET_TOKEN_EXPIRE_MINUTES} minutes.</p>"
        f'<p><a href="{reset_link}" style="display:inline-block;padding:10px 18px;'
        'background:#16a34a;color:#ffffff;text-decoration:none;border-radius:6px;'
        f'font-weight:600;">Reset my password</a></p>'
        f'<p>Or copy and paste this URL into your browser:<br/>'
        f'<span style="word-break:break-all;color:#475569;">{reset_link}</span></p>'
        "<p>If you did not request this, you can safely ignore this email — your "
        "password will not change.</p>"
        "<p>— DoItEasy Support</p>"
    )

    try:
        result = await call_whmcs(
            "SendEmail",
            {
                "id": int(client_id),
                "customtype": "general",
                "customsubject": subject,
                "custommessage": html_body,
            },
        )
        ok = str(result.get("result", "")).lower() == "success"
        if not ok:
            logger.warning(
                "WHMCS SendEmail did not report success for client_hash=%s: %s",
                hashlib.sha256(str(client_id).encode()).hexdigest()[:8],
                result.get("message") or result,
            )
        return ok
    except HTTPException:
        raise
    except Exception as exc:  # noqa: BLE001 — defensive: never leak to caller
        logger.exception("Failed to dispatch WHMCS password-reset email: %s", exc)
        return False


async def sync_password_to_whmcs(client_id: int, new_password: str) -> bool:
    """Sync a new password to the linked WHMCS client via UpdateClient."""
    try:
        result = await call_whmcs(
            "UpdateClient",
            {"clientid": int(client_id), "password2": new_password},
        )
        ok = str(result.get("result", "")).lower() == "success"
        if not ok:
            logger.warning(
                "WHMCS UpdateClient(password2) did not report success: %s",
                result.get("message") or result,
            )
        return ok
    except HTTPException:
        raise
    except Exception as exc:  # noqa: BLE001
        logger.exception("Failed to sync password to WHMCS: %s", exc)
        return False