import { SignJWT, importPKCS8 } from "jose";
import { getConfig } from "./config.js";
const FETCH_TIMEOUT_MS = 10_000;
let accessToken = null;
export async function sendPush(msg) {
    const fcm = getConfig().fcm;
    if (!fcm) {
        console.log(`[notify] fcm disabled: would send to ${msg.token.slice(0, 12)}…`, JSON.stringify(msg.data));
        return { result: "sent" };
    }
    let bearer;
    try {
        bearer = await getAccessToken(fcm);
    }
    catch (err) {
        console.error("[notify] fcm token exchange failed", err);
        return { result: "retryable", reason: `token exchange failed: ${describe(err)}` };
    }
    let res;
    try {
        res = await fetch(`https://fcm.googleapis.com/v1/projects/${fcm.projectId}/messages:send`, {
            method: "POST",
            headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
            body: JSON.stringify({
                message: {
                    token: msg.token,
                    data: msg.data,
                    android: {
                        priority: msg.priority.toUpperCase(),
                        ...(msg.collapseKey ? { collapse_key: msg.collapseKey } : {}),
                    },
                },
            }),
            signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
        });
    }
    catch (err) {
        return { result: "retryable", reason: `no response from fcm: ${describe(err)}` };
    }
    if (res.ok)
        return { result: "sent" };
    const text = await res.text().catch(() => "");
    const reason = `fcm ${res.status}: ${text.slice(0, 300)}`;
    if (res.status === 404 || (res.status === 400 && text.includes("INVALID_ARGUMENT"))) {
        return { result: "unregistered", reason };
    }
    if (res.status === 429 || res.status >= 500)
        return { result: "retryable", reason };
    console.error(`[notify] fcm send failed ${res.status}: ${text.slice(0, 300)}`);
    return { result: "failed", reason };
}
function describe(err) {
    if (err instanceof Error)
        return `${err.name}: ${err.message}`;
    return String(err);
}
async function getAccessToken(fcm) {
    const now = Date.now();
    if (accessToken && accessToken.expiresAt > now + 60_000)
        return accessToken.value;
    const key = await importPKCS8(fcm.privateKeyPem, "RS256");
    const assertion = await new SignJWT({
        scope: "https://www.googleapis.com/auth/firebase.messaging",
    })
        .setProtectedHeader({ alg: "RS256", typ: "JWT" })
        .setIssuer(fcm.clientEmail)
        .setAudience("https://oauth2.googleapis.com/token")
        .setIssuedAt()
        .setExpirationTime("1h")
        .sign(key);
    const res = await fetch("https://oauth2.googleapis.com/token", {
        method: "POST",
        headers: { "content-type": "application/x-www-form-urlencoded" },
        body: new URLSearchParams({
            grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
            assertion,
        }),
        signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
    });
    if (!res.ok)
        throw new Error(`token endpoint answered ${res.status}`);
    const json = (await res.json());
    accessToken = { value: json.access_token, expiresAt: now + json.expires_in * 1000 };
    return json.access_token;
}
