import { beforeEach, describe, expect, it, vi } from "vitest"
import { PushAppState, PushPlatform } from "@prisma/client"

const { txMock, prismaMock } = vi.hoisted(() => {
    const txMock = {
        posPushInstallation: {
            findUnique: vi.fn(),
            delete: vi.fn(),
            update: vi.fn(),
            create: vi.fn(),
        },
    }

    const prismaMock = {
        $transaction: vi.fn(async (callback: (tx: typeof txMock) => Promise<unknown>) => callback(txMock)),
        posPushInstallation: {
            findUnique: vi.fn(),
            update: vi.fn(),
            findFirst: vi.fn(),
            findMany: vi.fn(),
        },
    }

    return { txMock, prismaMock }
})

vi.mock("@/core/prisma", () => ({
    prisma: prismaMock,
}))

import { pushService } from "./push.service"

describe("pushService", () => {
    beforeEach(() => {
        vi.clearAllMocks()
    })

    it("rebind correctement une installation existante au nouvel utilisateur du même terminal", async () => {
        const existing = {
            id: "install-1",
            shopId: "shop-old",
            shopUserId: "user-old",
            terminalId: "terminal-1",
            fcmToken: "token-old",
            platform: PushPlatform.ANDROID,
            appVersion: "0.9.0",
            isActive: true,
            appState: PushAppState.BACKGROUND,
            lastSeenAt: new Date("2026-05-08T10:00:00.000Z"),
            lastForegroundAt: null,
            createdAt: new Date("2026-05-08T09:00:00.000Z"),
            updatedAt: new Date("2026-05-08T09:00:00.000Z"),
        }

        txMock.posPushInstallation.findUnique
            .mockResolvedValueOnce(existing)
            .mockResolvedValueOnce(null)
        txMock.posPushInstallation.update.mockResolvedValue({
            ...existing,
            shopId: "shop-new",
            shopUserId: "user-new",
            fcmToken: "token-new",
            appState: PushAppState.FOREGROUND,
            lastSeenAt: new Date("2026-05-08T12:00:00.000Z"),
            lastForegroundAt: new Date("2026-05-08T12:00:00.000Z"),
            updatedAt: new Date("2026-05-08T12:00:00.000Z"),
        })

        const result = await pushService.registerInstallation({
            shopId: "shop-new",
            shopUserId: "user-new",
            terminalId: "terminal-1",
            fcmToken: "token-new",
            platform: PushPlatform.ANDROID,
            appVersion: "1.0.0",
            appState: PushAppState.FOREGROUND,
        })

        expect(txMock.posPushInstallation.update).toHaveBeenCalledTimes(1)
        expect(txMock.posPushInstallation.update).toHaveBeenCalledWith(expect.objectContaining({
            where: { id: "install-1" },
            data: expect.objectContaining({
                shopId: "shop-new",
                shopUserId: "user-new",
                terminalId: "terminal-1",
                fcmToken: "token-new",
                isActive: true,
                appState: PushAppState.FOREGROUND,
            }),
        }))
        expect(result.ok).toBe(true)
        expect(result.installation.shopUserId).toBe("user-new")
        expect(result.installation.terminalId).toBe("terminal-1")
    })

    it("désactive et dissocie l'installation au unregister", async () => {
        prismaMock.posPushInstallation.findUnique.mockResolvedValue({
            id: "install-1",
            shopId: "shop-1",
            shopUserId: "user-1",
            terminalId: "terminal-1",
        })
        prismaMock.posPushInstallation.update.mockResolvedValue({
            id: "install-1",
            terminalId: "terminal-1",
            isActive: false,
        })

        const result = await pushService.unregisterInstallation({
            shopId: "shop-1",
            shopUserId: "user-1",
            terminalId: "terminal-1",
        })

        expect(prismaMock.posPushInstallation.update).toHaveBeenCalledWith(expect.objectContaining({
            where: { id: "install-1" },
            data: expect.objectContaining({
                isActive: false,
                shopUserId: null,
                appState: PushAppState.UNKNOWN,
            }),
        }))
        expect(result).toEqual({
            ok: true,
            terminalId: "terminal-1",
            isActive: false,
        })
    })
})

