import { AppError } from "@/core/AppError"
import { prisma } from "@/core/prisma"
import { tracked } from "@/core/tracked"
import type { CreateDepositDefinitionInput, UpdateDepositDefinitionInput } from "ttm-shared"

function mapDepositDefinition(definition: {
    id: string
    shopId: string
    label: string
    amount: number
    active: boolean
    displayOrder: number
    code: string | null
    description: string | null
}) {
    return {
        id: definition.id,
        shopId: definition.shopId,
        label: definition.label,
        amount: definition.amount,
        active: definition.active,
        displayOrder: definition.displayOrder,
        code: definition.code ?? null,
        description: definition.description ?? null,
    }
}

export const depositDefinitionService = {
    async listByShop(shopId: string, options?: { activeOnly?: boolean }) {
        const definitions = await prisma.depositDefinition.findMany({
            where: {
                shopId,
                ...(options?.activeOnly ? { active: true } : {}),
            },
            orderBy: [
                { displayOrder: "asc" },
                { label: "asc" },
            ],
        })

        return definitions.map(mapDepositDefinition)
    },

    async findById(shopId: string, id: string) {
        const definition = await prisma.depositDefinition.findFirst({
            where: { shopId, id },
        })

        return definition ? mapDepositDefinition(definition) : null
    },

    async create(shopId: string, input: CreateDepositDefinitionInput, userId?: string, senderSocketId?: string) {
        const existing = await prisma.depositDefinition.findFirst({
            where: {
                shopId,
                label: input.label,
            },
            select: { id: true },
        })

        if (existing) {
            throw new AppError("A deposit definition with this label already exists", 409)
        }

        const created = await prisma.depositDefinition.create({
            data: {
                shopId,
                label: input.label,
                amount: input.amount,
                active: input.active ?? true,
                displayOrder: input.displayOrder ?? 0,
                code: input.code ?? null,
                description: input.description ?? null,
            },
        })

        const t = tracked({ shopId, entity: "DepositDefinition", actorUserId: userId, senderSocketId })
        await t.emit(
            "deposit-definition.created",
            {
                depositDefinitionId: created.id,
                label: created.label,
                amount: created.amount,
                active: created.active,
            },
            created.id,
            1,
        )

        return mapDepositDefinition(created)
    },

    async update(shopId: string, id: string, input: UpdateDepositDefinitionInput, userId?: string, senderSocketId?: string) {
        const existing = await prisma.depositDefinition.findFirst({
            where: { shopId, id },
        })

        if (!existing) {
            throw new AppError("Deposit definition not found", 404)
        }

        if (input.label && input.label !== existing.label) {
            const duplicate = await prisma.depositDefinition.findFirst({
                where: {
                    shopId,
                    label: input.label,
                    id: { not: id },
                },
                select: { id: true },
            })

            if (duplicate) {
                throw new AppError("A deposit definition with this label already exists", 409)
            }
        }

        const updated = await prisma.depositDefinition.update({
            where: { id },
            data: {
                ...(input.label !== undefined ? { label: input.label } : {}),
                ...(input.amount !== undefined ? { amount: input.amount } : {}),
                ...(input.active !== undefined ? { active: input.active } : {}),
                ...(input.displayOrder !== undefined ? { displayOrder: input.displayOrder } : {}),
                ...(input.code !== undefined ? { code: input.code ?? null } : {}),
                ...(input.description !== undefined ? { description: input.description ?? null } : {}),
            },
        })

        const t = tracked({ shopId, entity: "DepositDefinition", actorUserId: userId, senderSocketId })
        await t.emit(
            "deposit-definition.updated",
            {
                depositDefinitionId: updated.id,
                changes: Object.keys(input),
                active: updated.active,
            },
            updated.id,
            1,
        )

        return mapDepositDefinition(updated)
    },

    async softDelete(shopId: string, id: string, userId?: string, senderSocketId?: string) {
        const existing = await prisma.depositDefinition.findFirst({
            where: { shopId, id },
        })

        if (!existing) {
            throw new AppError("Deposit definition not found", 404)
        }

        const updated = await prisma.depositDefinition.update({
            where: { id },
            data: { active: false },
        })

        const t = tracked({ shopId, entity: "DepositDefinition", actorUserId: userId, senderSocketId })
        await t.emit(
            "deposit-definition.deleted",
            {
                depositDefinitionId: updated.id,
                label: updated.label,
            },
            updated.id,
            1,
        )

        return mapDepositDefinition(updated)
    },
}

