"""FCM push notifications (HTTP v1 API) — best-effort, never breaks a caller.

No firebase-admin dependency: the OAuth2 access token FCM's v1 API requires is
obtained by signing a JWT with the service account's own private key (RS256,
via PyJWT — already a dependency) and exchanging it at Google's token
endpoint, rather than pulling in the full Firebase Admin SDK.

Blocked until a Firebase project + service account credentials exist (a
Firebase project needs to be created by the app owner — not something that
can be generated here). Until then, get_setting() returns nothing and every
call is a clean no-op — see core.notify.push_notification, which always
writes the in-app Notification regardless of whether push is configured.
"""

from __future__ import annotations

import json
import time

import jwt
import requests
from django.core.cache import cache

from .settings_store import get_setting

_TOKEN_CACHE_KEY = "fcm:access_token"
_FCM_SCOPE = "https://www.googleapis.com/auth/firebase.messaging"
_TOKEN_URL = "https://oauth2.googleapis.com/token"
_REQUEST_TIMEOUT_SECONDS = 10


def _service_account() -> dict | None:
    raw = get_setting("fcm_service_account_json", "FCM_SERVICE_ACCOUNT_JSON")
    if not raw:
        return None
    try:
        account = json.loads(raw)
    except ValueError:
        return None
    if not account.get("client_email") or not account.get("private_key") or not account.get("project_id"):
        return None
    return account


def is_configured() -> bool:
    return _service_account() is not None


def _get_access_token(account: dict) -> str:
    cached = cache.get(_TOKEN_CACHE_KEY)
    if cached:
        return cached

    now = int(time.time())
    assertion = jwt.encode(
        {
            "iss": account["client_email"],
            "scope": _FCM_SCOPE,
            "aud": _TOKEN_URL,
            "iat": now,
            "exp": now + 3600,
        },
        account["private_key"],
        algorithm="RS256",
    )
    response = requests.post(
        _TOKEN_URL,
        data={
            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
            "assertion": assertion,
        },
        timeout=_REQUEST_TIMEOUT_SECONDS,
    )
    response.raise_for_status()
    token = response.json()["access_token"]
    cache.set(_TOKEN_CACHE_KEY, token, timeout=3300)  # refresh a bit before the 1h expiry
    return token


def send_push(device_token: str, title: str, body: str, *, data: dict | None = None) -> bool:
    """Best-effort single-device push. Returns True only on confirmed FCM
    acceptance; False (never raises) if unconfigured, unreachable, or
    rejected — a push failure must never break the caller's main flow."""
    account = _service_account()
    if account is None:
        return False
    try:
        access_token = _get_access_token(account)
        response = requests.post(
            f"https://fcm.googleapis.com/v1/projects/{account['project_id']}/messages:send",
            json={
                "message": {
                    "token": device_token,
                    "notification": {"title": title, "body": body},
                    "data": {str(k): str(v) for k, v in (data or {}).items()},
                }
            },
            headers={"Authorization": f"Bearer {access_token}"},
            timeout=_REQUEST_TIMEOUT_SECONDS,
        )
        return response.status_code < 300
    except (requests.RequestException, KeyError, ValueError):
        return False
