import { cert, getApp, getApps, initializeApp } from "firebase-admin/app"
import { getMessaging } from "firebase-admin/messaging"

type PushTokenResult = {
    token: string
    success: boolean
    invalidToken: boolean
    errorCode: string | null
}

export type PushSendResult = {
    mode: "sent" | "noop"
    reason: string | null
    successCount: number
    failureCount: number
    results: PushTokenResult[]
}

function normalizePrivateKey(value: string) {
    return value.replace(/\\n/g, "\n")
}

function getFirebaseCredentials() {
    const serviceAccountJson = process.env.FIREBASE_SERVICE_ACCOUNT_JSON?.trim()
    if (serviceAccountJson) {
        try {
            const parsed = JSON.parse(serviceAccountJson) as {
                project_id?: string
                client_email?: string
                private_key?: string
            }

            if (parsed.project_id && parsed.client_email && parsed.private_key) {
                return {
                    projectId: parsed.project_id,
                    clientEmail: parsed.client_email,
                    privateKey: normalizePrivateKey(parsed.private_key),
                }
            }
        } catch (error) {
            console.warn("[push:fcm] FIREBASE_SERVICE_ACCOUNT_JSON invalide", error)
        }
    }

    const projectId = process.env.FIREBASE_PROJECT_ID?.trim()
    const clientEmail = process.env.FIREBASE_CLIENT_EMAIL?.trim()
    const privateKey = process.env.FIREBASE_PRIVATE_KEY?.trim()

    if (!projectId || !clientEmail || !privateKey) {
        return null
    }

    return {
        projectId,
        clientEmail,
        privateKey: normalizePrivateKey(privateKey),
    }
}

function getFirebaseApp() {
    const credentials = getFirebaseCredentials()
    if (!credentials) return null

    const existing = getApps().find((app) => app.name === "ttm-push")
    if (existing) return getApp("ttm-push")

    return initializeApp({
        credential: cert({
            projectId: credentials.projectId,
            clientEmail: credentials.clientEmail,
            privateKey: credentials.privateKey,
        }),
    }, "ttm-push")
}

function maskToken(token: string) {
    return token.length <= 10 ? token : `…${token.slice(-10)}`
}

function extractErrorCode(error: unknown): string | null {
    if (typeof error === "object" && error && "code" in error && typeof (error as { code: unknown }).code === "string") {
        return (error as { code: string }).code
    }

    return null
}

function isInvalidTokenCode(code: string | null) {
    return code === "messaging/invalid-registration-token"
        || code === "messaging/registration-token-not-registered"
}

export const pushFcmService = {
    async sendDataMessage(input: { tokens: string[]; data: Record<string, string> }): Promise<PushSendResult> {
        const uniqueTokens = [...new Set(input.tokens.filter(Boolean))]
        if (uniqueTokens.length === 0) {
            return {
                mode: "noop",
                reason: "no_tokens",
                successCount: 0,
                failureCount: 0,
                results: [],
            }
        }

        if (process.env.PUSH_FCM_ENABLED === "false") {
            console.warn(`[push:fcm] disabled by env, skipping ${uniqueTokens.length} token(s)`)
            return {
                mode: "noop",
                reason: "disabled",
                successCount: 0,
                failureCount: 0,
                results: [],
            }
        }

        const app = getFirebaseApp()
        if (!app) {
            console.warn(`[push:fcm] missing firebase configuration, skipping ${uniqueTokens.length} token(s)`)
            return {
                mode: "noop",
                reason: "missing_config",
                successCount: 0,
                failureCount: 0,
                results: [],
            }
        }

        const response = await getMessaging(app).sendEachForMulticast({
            tokens: uniqueTokens,
            data: input.data,
            android: { priority: "high" },
        })

        const results = response.responses.map((entry, index) => {
            const token = uniqueTokens[index] ?? ""
            const errorCode = extractErrorCode(entry.error)
            return {
                token,
                success: entry.success,
                invalidToken: isInvalidTokenCode(errorCode),
                errorCode,
            }
        })

        console.info(`[push:fcm] sent=${response.successCount} failed=${response.failureCount} tokens=${uniqueTokens.map(maskToken).join(",")}`)

        return {
            mode: "sent",
            reason: null,
            successCount: response.successCount,
            failureCount: response.failureCount,
            results,
        }
    },
}

