"""Razorpay adapter — the ONLY module that touches the Razorpay SDK.

Everything above this layer (payment service, premium service, routes) is
provider-agnostic and calls these thin functions. Swapping or adding a provider
means adding a sibling adapter, not editing business logic.

The SDK is imported lazily inside each function so the application boots even when
``razorpay`` is not installed or no keys are configured — the online upgrade flow
is simply unavailable until both are present. Tests monkeypatch these functions,
so they never require the SDK or network access.
"""

from __future__ import annotations

from flask import current_app

PROVIDER = "razorpay"


class PaymentConfigError(RuntimeError):
    """Raised when Razorpay credentials (or the SDK) are unavailable."""


class SignatureError(Exception):
    """Raised when a payment or webhook signature fails verification."""


def _keys() -> tuple[str, str]:
    key_id = current_app.config.get("RAZORPAY_KEY_ID") or ""
    key_secret = current_app.config.get("RAZORPAY_KEY_SECRET") or ""
    if not (key_id and key_secret):
        raise PaymentConfigError("Razorpay keys are not configured.")
    return key_id, key_secret


def _client():
    """Build an authenticated Razorpay client (lazy SDK import)."""
    key_id, key_secret = _keys()
    try:
        import razorpay  # imported lazily — optional dependency
    except ImportError as exc:  # pragma: no cover - environment-dependent
        raise PaymentConfigError("The razorpay SDK is not installed.") from exc
    return razorpay.Client(auth=(key_id, key_secret))


def create_order(amount_paise: int, currency: str, receipt: str, notes: dict | None = None) -> dict:
    """Create a Razorpay order and return the gateway's order dict.

    ``amount_paise`` is the amount in the currency's minor unit (paise for INR) —
    the server computes it from the plan price; the browser never supplies it.
    """
    client = _client()
    payload = {
        "amount": int(amount_paise),
        "currency": currency,
        "receipt": receipt,
        "payment_capture": 1,  # auto-capture authorized payments
    }
    if notes:
        payload["notes"] = notes
    return client.order.create(data=payload)


def verify_payment_signature(order_id: str, payment_id: str, signature: str) -> bool:
    """Verify a Checkout callback signature via the SDK. False on any mismatch."""
    client = _client()
    try:
        client.utility.verify_payment_signature(
            {
                "razorpay_order_id": order_id,
                "razorpay_payment_id": payment_id,
                "razorpay_signature": signature,
            }
        )
        return True
    except Exception:  # SDK raises SignatureVerificationError on mismatch
        return False


def verify_webhook_signature(body: str, signature: str) -> bool:
    """Verify a webhook payload signature against ``RAZORPAY_WEBHOOK_SECRET``.

    ``body`` must be the raw request body (bytes decoded to str), unmodified.
    Returns False when the secret is missing or the signature does not match.
    """
    secret = current_app.config.get("RAZORPAY_WEBHOOK_SECRET") or ""
    if not secret:
        return False
    client = _client()
    try:
        client.utility.verify_webhook_signature(body, signature, secret)
        return True
    except Exception:
        return False
