import os
from typing import Optional, Dict, List, Callable

import firebase_admin
from firebase_admin import credentials, messaging
from flask import current_app

_firebase_app = None


def init_firebase():
    global _firebase_app
    if _firebase_app:
        return _firebase_app

    path = (
        current_app.config.get("FIREBASE_SERVICE_ACCOUNT_JSON")
        or os.environ.get("FIREBASE_SERVICE_ACCOUNT_JSON")
    )
    if not path:
        raise RuntimeError("FIREBASE_SERVICE_ACCOUNT_JSON not set")

    cred = credentials.Certificate(path)
    _firebase_app = firebase_admin.initialize_app(cred)
    return _firebase_app


def send_push(
    tokens: List[str],
    title: str,
    body: str,
    data: Optional[Dict[str, str]] = None,
    on_invalid_tokens: Optional[Callable[[List[str]], None]] = None,
):
    """
    Multicast push via FCM.
    - Supports many tokens.
    - Won't crash if some tokens are invalid.
    - Optionally calls on_invalid_tokens([...]) with tokens that should be deactivated.
    """
    tokens = [t for t in (tokens or []) if t]
    if not tokens:
        return {"sent": 0, "failed": 0, "invalid": 0}

    init_firebase()

    # FCM data must be string->string
    data = {k: str(v) for k, v in (data or {}).items()}

    msg = messaging.MulticastMessage(
        notification=messaging.Notification(title=title, body=body),
        data=data,
        tokens=tokens,
    )

    resp = messaging.send_each_for_multicast(msg)
    for i, r in enumerate(resp.responses):
        if not r.success:
            print("PUSH ERROR TOKEN:", tokens[i])
            print("PUSH ERROR EXCEPTION:", repr(r.exception))
            print("PUSH ERROR STRING:", str(r.exception))

    invalid_tokens: List[str] = []

    # Identify tokens that are no longer valid
    # Common cases:
    # - messaging.UnregisteredError (token not registered)
    # - "registration-token-not-registered"
    # - "invalid-argument"
    for i, r in enumerate(resp.responses):
        if r.success:
            continue

        tok = tokens[i]
        err = r.exception

        # Best-effort classification (firebase_admin error types vary by version)
        code = ""
        try:
            code = getattr(err, "code", "") or ""
        except Exception:
            code = ""

        msg_txt = str(err or "")

        # mark as invalid for these common patterns
        if (
            "registration-token-not-registered" in msg_txt.lower()
            or "not registered" in msg_txt.lower()
            or "unregistered" in msg_txt.lower()
            or "invalid-argument" in msg_txt.lower()
            or code in ("UNREGISTERED", "INVALID_ARGUMENT")
        ):
            invalid_tokens.append(tok)

    if invalid_tokens and on_invalid_tokens:
        try:
            on_invalid_tokens(invalid_tokens)
        except Exception:
            # never break request due to cleanup
            current_app.logger.exception("Failed to deactivate invalid tokens")

    return {
        "sent": resp.success_count,
        "failed": resp.failure_count,
        "invalid": len(invalid_tokens),
    }