import { prisma } from "@/core/prisma"
import { AppError } from "@/core/AppError"

export const shopEventService = {
    /**
     * Incrémente le séquenceur et retourne le prochain numéro d'événement
     */
    async nextSequence(shopId: string): Promise<bigint> {
        const seq = await prisma.shopEventSeq.upsert({
            where: { shopId },
            create: {
                shopId,
                seq: 1n
            },
            update: {
                seq: {
                    increment: 1
                }
            }
        })
        return seq.seq
    },

    /**
     * Crée un nouvel événement ShopEvent de manière atomique
     */
    async createEvent(
        shopId: string,
        type: string,
        payload: Record<string, any>,
        options?: {
            entity?: string
            entityId?: string
            revision?: number
            actorUserId?: string | null
            deviceId?: string | null
        }
    ) {
        const seq = await this.nextSequence(shopId)

        const event = await prisma.shopEvent.create({
            data: {
                shopId,
                seq,
                type,
                payload,
                entity: options?.entity,
                entityId: options?.entityId,
                revision: options?.revision,
                actorUserId: options?.actorUserId,
                deviceId: options?.deviceId
            }
        })

        console.log("Created Shop Event", event)

        return event
    },

    /**
     * Récupère les événements depuis une séquence donnée
     * Retourne 410 Gone si l'historique est trop ancien
     */
    async queryEventsSince(
        shopId: string,
        since: bigint | string,
        limit: number = 100
    ) {
        const sinceBigInt = typeof since === "string" ? BigInt(since) : since

        // Vérifier si des événements existent à partir de cette séquence
        const oldestEvent = await prisma.shopEvent.findFirst({
            where: { shopId },
            orderBy: { seq: "asc" },
            select: { seq: true }
        })

        // Si on demande une séquence plus ancienne que le plus vieil événement
        if (oldestEvent && sinceBigInt < oldestEvent.seq) {
            return {
                status: 410 as const,
                message: "Event history expired - snapshot required",
                action: "SNAPSHOT_REQUIRED" as const
            }
        }

        // Récupérer les événements
        const events = await prisma.shopEvent.findMany({
            where: {
                shopId,
                seq: { gt: sinceBigInt }
            },
            orderBy: { seq: "asc" },
            take: limit + 1  // +1 pour déterminer hasMore
        })

        const hasMore = events.length > limit
        const result = events.slice(0, limit)

        return {
            status: 200 as const,
            events: result.map(e => ({
                id: e.id,
                shopId: e.shopId,
                seq: e.seq.toString(),
                type: e.type,
                entity: e.entity,
                entityId: e.entityId,
                revision: e.revision,
                payload: e.payload,
                actorUserId: e.actorUserId,
                deviceId: e.deviceId,
                createdAt: e.createdAt.toISOString(),
                dispatchedAt: e.dispatchedAt?.toISOString() ?? null
            })),
            from: (sinceBigInt + 1n).toString(),
            to: result.length > 0 ? result[result.length - 1].seq.toString() : sinceBigInt.toString(),
            hasMore
        }
    },

    /**
     * Récupère le numéro de séquence le plus élevé pour un shop
     */
    async getLatestSeq(shopId: string): Promise<bigint> {
        const latest = await prisma.shopEvent.findFirst({
            where: { shopId },
            orderBy: { seq: "desc" },
            select: { seq: true }
        })
        return latest?.seq ?? 0n
    },

    /**
     * Marque un événement comme dispatché via WebSocket
     */
    async markDispatched(eventId: string) {
        return prisma.shopEvent.update({
            where: { id: eventId },
            data: { dispatchedAt: new Date() }
        })
    }
}

