import { PushAppState } from "@prisma/client"
import { shopEventService } from "@/modules/shop/shop-event.service"
import { pushFcmService } from "./push-fcm.service"
import { pushService } from "./push.service"

export type OrderItemReadyPushCandidate = {
    shopId: string
    orderId: string
    orderItemId: string
    targetShopUserId: string | null
    productLabel?: string | null
    orderShortId?: string | null
    readyStationId?: string | null
    readyStationName?: string | null
    cashStationId?: string | null
}

type PushDispatchDecision =
    | "SKIP_NO_TARGET_USER"
    | "SKIP_NO_INSTALLATION"
    | "SKIP_FOREGROUND_PRESENT"
    | "SKIP_NO_BACKGROUND_TARGET"
    | "SEND"
    | "SEND_NOOP_FCM_DISABLED"

function normalizePushText(value: string | null | undefined) {
    return value?.trim() ?? ""
}

function truncatePushText(value: string, maxLength: number) {
    const normalized = normalizePushText(value)
    if (normalized.length <= maxLength) return normalized
    if (maxLength <= 1) return normalized.slice(0, maxLength)
    return `${normalized.slice(0, maxLength - 1).trimEnd()}…`
}

function buildFallbackOrderShortId(orderId: string) {
    return `#${orderId.slice(0, 8)}`
}

function buildCashierOrderTargetPath(orderId: string, cashStationId: string | null | undefined) {
    const encodedOrderId = encodeURIComponent(orderId)
    const query = normalizePushText(cashStationId)
        ? `?stationId=${encodeURIComponent(normalizePushText(cashStationId))}`
        : ""

    return `/pos/stations/cashier/orders/${encodedOrderId}${query}`
}

function buildOrderItemReadyPushData(input: OrderItemReadyPushCandidate) {
    const orderShortId = normalizePushText(input.orderShortId) || buildFallbackOrderShortId(input.orderId)
    const productLabel = truncatePushText(normalizePushText(input.productLabel) || "Produit", 72)
    const readyStationName = truncatePushText(normalizePushText(input.readyStationName) || "Production", 48)

    return {
        event: "order_item_ready",
        pushSchemaVersion: "2",
        shopId: input.shopId,
        orderId: input.orderId,
        orderItemId: input.orderItemId,
        targetShopUserId: input.targetShopUserId ?? "",
        productLabel,
        orderShortId,
        readyStationId: input.readyStationId ?? "",
        readyStationName,
        cashierStationId: input.cashStationId ?? "",
        notificationTitle: `Produit prêt · ${orderShortId}`,
        notificationBody: `${productLabel} prêt à ${readyStationName}`,
        clickAction: "open_cashier_order_detail",
        targetRouteName: "cashier-order-detail",
        targetRoutePath: buildCashierOrderTargetPath(input.orderId, input.cashStationId),
        targetPhase: "order-detail",
        targetOrderId: input.orderId,
        targetStationId: input.cashStationId ?? "",
    }
}

function logDecision(input: {
    shopId: string
    orderId: string
    orderItemId: string
    targetShopUserId: string | null
    eligibleInstallationCount: number
    foregroundInstallationCount: number
    decision: PushDispatchDecision
}) {
    console.info("[push] event=push.order_item_ready.evaluate", input)
}

async function persistPushDecision(input: {
    shopId: string
    orderId: string
    orderItemId: string
    targetShopUserId: string | null
    eligibleInstallationCount: number
    foregroundInstallationCount: number
    decision: PushDispatchDecision
    successCount?: number
    failureCount?: number
}) {
    try {
        await shopEventService.createEvent(
            input.shopId,
            "push.order_item_ready.evaluated",
            {
                orderId: input.orderId,
                orderItemId: input.orderItemId,
                targetShopUserId: input.targetShopUserId,
                eligibleInstallationCount: input.eligibleInstallationCount,
                foregroundInstallationCount: input.foregroundInstallationCount,
                decision: input.decision,
                successCount: input.successCount ?? null,
                failureCount: input.failureCount ?? null,
            },
            {
                entity: "OrderItem",
                entityId: input.orderItemId,
                actorUserId: input.targetShopUserId,
            },
        )
    } catch (error) {
        console.warn("[push] unable to persist push decision", error)
    }
}

export const posPushDispatchService = {
    async dispatchOrderItemReady(input: OrderItemReadyPushCandidate) {
        if (!input.targetShopUserId) {
            const decisionInput = {
                ...input,
                eligibleInstallationCount: 0,
                foregroundInstallationCount: 0,
                decision: "SKIP_NO_TARGET_USER",
            } as const
            logDecision(decisionInput)
            await persistPushDecision(decisionInput)
            return { ok: true as const, decision: "SKIP_NO_TARGET_USER" as const }
        }

        const installations = await pushService.listEligibleInstallations(input.shopId, input.targetShopUserId)
        if (installations.length === 0) {
            const decisionInput = {
                ...input,
                eligibleInstallationCount: 0,
                foregroundInstallationCount: 0,
                decision: "SKIP_NO_INSTALLATION",
            } as const
            logDecision(decisionInput)
            await persistPushDecision(decisionInput)
            return { ok: true as const, decision: "SKIP_NO_INSTALLATION" as const }
        }

        const foregroundInstallations = installations.filter((installation) => installation.appState === PushAppState.FOREGROUND)
        if (foregroundInstallations.length > 0) {
            const decisionInput = {
                ...input,
                eligibleInstallationCount: installations.length,
                foregroundInstallationCount: foregroundInstallations.length,
                decision: "SKIP_FOREGROUND_PRESENT",
            } as const
            logDecision(decisionInput)
            await persistPushDecision(decisionInput)
            return { ok: true as const, decision: "SKIP_FOREGROUND_PRESENT" as const }
        }

        const backgroundInstallations = installations.filter((installation) => installation.appState !== PushAppState.FOREGROUND)
        if (backgroundInstallations.length === 0) {
            const decisionInput = {
                ...input,
                eligibleInstallationCount: installations.length,
                foregroundInstallationCount: 0,
                decision: "SKIP_NO_BACKGROUND_TARGET",
            } as const
            logDecision(decisionInput)
            await persistPushDecision(decisionInput)
            return { ok: true as const, decision: "SKIP_NO_BACKGROUND_TARGET" as const }
        }

        const sendResult = await pushFcmService.sendDataMessage({
            tokens: backgroundInstallations.map((installation) => installation.fcmToken),
            data: buildOrderItemReadyPushData(input),
        })

        if (sendResult.mode === "noop") {
            const decisionInput = {
                ...input,
                eligibleInstallationCount: backgroundInstallations.length,
                foregroundInstallationCount: 0,
                decision: "SEND_NOOP_FCM_DISABLED",
            } as const
            logDecision(decisionInput)
            await persistPushDecision(decisionInput)
            return { ok: true as const, decision: "SEND_NOOP_FCM_DISABLED" as const, sendResult }
        }

        const installationsByToken = new Map(backgroundInstallations.map((installation) => [installation.fcmToken, installation]))
        await Promise.all(sendResult.results.map(async (result) => {
            const installation = installationsByToken.get(result.token)
            if (!installation) return

            if (result.success) {
                await pushService.markPushDelivered(installation.id, "order_item_ready")
                return
            }

            await pushService.markPushFailure(installation.id, result.errorCode, result.invalidToken)
        }))

        const decisionInput = {
            ...input,
            eligibleInstallationCount: backgroundInstallations.length,
            foregroundInstallationCount: 0,
            decision: "SEND",
            successCount: sendResult.successCount,
            failureCount: sendResult.failureCount,
        } as const
        logDecision(decisionInput)
        await persistPushDecision(decisionInput)

        return {
            ok: true as const,
            decision: "SEND" as const,
            sendResult,
        }
    },
}

