import { FastifyInstance } from "fastify"
import { and, eq, isNull } from "drizzle-orm"
import { authUsers, notificationMobilePushDevices } from "../../db/schema"
import { NotificationService, UserDirectory } from "../modules/notification.service"
import { pushServerClient } from "../modules/push-server.client"

const getUserDirectory: UserDirectory = async (server: FastifyInstance, userId) => {
    const rows = await server.db
        .select({ email: authUsers.email })
        .from(authUsers)
        .where(eq(authUsers.id, userId))
        .limit(1)
    const data = rows[0]
    if (!data) return null
    return { email: data.email }
}

const requireTenant = (tenantId: number | null) => {
    if (!tenantId) throw new Error("Kein aktiver Mandant")
    return tenantId
}

export default async function notificationsRoutes(server: FastifyInstance) {
    const svc = new NotificationService(server, getUserDirectory)

    server.get("/notifications", async (req) => {
        const limit = Number((req.query as { limit?: string })?.limit || 50)
        return await svc.listForUser(requireTenant(req.user.tenant_id), req.user.user_id, limit)
    })

    server.post("/notifications/:id/read", async (req, reply) => {
        const params = req.params as { id: string }
        const item = await svc.markRead(requireTenant(req.user.tenant_id), req.user.user_id, params.id)
        if (!item) return reply.code(404).send({ error: "Benachrichtigung nicht gefunden" })
        return item
    })

    server.get("/notifications/push/config", async () => {
        return svc.getPublicPushConfig()
    })

    server.post("/notifications/push/subscribe", async (req) => {
        const tenantId = requireTenant(req.user.tenant_id)
        const userAgent = req.headers["user-agent"]
        const subscription = await svc.registerPushSubscription(
            tenantId,
            req.user.user_id,
            req.body as any,
            Array.isArray(userAgent) ? userAgent.join(" ") : userAgent
        )

        return {
            success: true,
            id: subscription?.id,
        }
    })

    server.delete("/notifications/push/subscribe", async (req) => {
        const body = (req.body || {}) as { endpoint?: string }
        if (!body.endpoint) throw new Error("endpoint fehlt")
        return await svc.disablePushSubscription(requireTenant(req.user.tenant_id), req.user.user_id, body.endpoint)
    })

    server.post("/notifications/push/mobile/register", async (req) => {
        const tenantId = requireTenant(req.user.tenant_id)
        const body = (req.body || {}) as {
            localDeviceId?: string
            platform?: "ios" | "android"
            providerToken?: string
            deviceLabel?: string
            meta?: Record<string, unknown>
        }

        if (!body.localDeviceId) throw new Error("localDeviceId fehlt")
        if (body.platform !== "ios" && body.platform !== "android") throw new Error("platform ist ungültig")
        if (!body.providerToken) throw new Error("providerToken fehlt")

        const centralLocalDeviceId = `${tenantId}:${req.user.user_id}:${body.localDeviceId}`
        const registered = await pushServerClient.registerDevice({
            localDeviceId: centralLocalDeviceId,
            platform: body.platform,
            providerToken: body.providerToken,
            meta: {
                ...(body.meta || {}),
                tenantId,
                userId: req.user.user_id,
                source: "fedeo-mobile",
            },
        })

        const rows = await server.db
            .insert(notificationMobilePushDevices)
            .values({
                tenantId,
                userId: req.user.user_id,
                localDeviceId: body.localDeviceId,
                centralDeviceId: registered.centralDeviceId,
                platform: body.platform,
                providerTokenPreview: previewToken(body.providerToken),
                deviceLabel: body.deviceLabel,
                meta: body.meta ?? null,
                lastSeenAt: new Date(),
                disabledAt: null,
            })
            .onConflictDoUpdate({
                target: [
                    notificationMobilePushDevices.tenantId,
                    notificationMobilePushDevices.userId,
                    notificationMobilePushDevices.localDeviceId,
                ],
                set: {
                    centralDeviceId: registered.centralDeviceId,
                    platform: body.platform,
                    providerTokenPreview: previewToken(body.providerToken),
                    deviceLabel: body.deviceLabel,
                    meta: body.meta ?? null,
                    lastSeenAt: new Date(),
                    disabledAt: null,
                },
            })
            .returning()

        return {
            success: true,
            id: rows[0]?.id,
            centralDeviceId: registered.centralDeviceId,
            status: registered.status,
        }
    })

    server.post("/notifications/test-mobile-push", async (req) => {
        const tenantId = requireTenant(req.user.tenant_id)
        const devices = await server.db
            .select({ centralDeviceId: notificationMobilePushDevices.centralDeviceId })
            .from(notificationMobilePushDevices)
            .where(and(
                eq(notificationMobilePushDevices.tenantId, tenantId),
                eq(notificationMobilePushDevices.userId, req.user.user_id),
                isNull(notificationMobilePushDevices.disabledAt)
            ))

        if (!devices.length) {
            throw new Error("Kein registriertes mobiles Push-Gerät gefunden")
        }

        return await pushServerClient.sendPush({
            idempotencyKey: `mobile-test:${tenantId}:${req.user.user_id}:${Date.now()}`,
            devices: devices.map((device) => device.centralDeviceId),
            priority: "high",
            ttlSeconds: 600,
            notification: {
                title: "FEDEO Mobile Push ist aktiv",
                body: "Diese Testnachricht wurde über den zentralen Push-Server zugestellt.",
            },
            data: {
                type: "system.test_mobile_push",
                link: "/",
            },
        })
    })

    server.post("/notifications/test-push", async (req) => {
        return await svc.trigger({
            tenantId: requireTenant(req.user.tenant_id),
            userId: req.user.user_id,
            eventType: "system.test_push",
            title: "FEDEO Desktop Push ist aktiv",
            message: "Diese Testbenachrichtigung wurde von FEDEO selbst zugestellt.",
            payload: {
                link: "/",
                icon: "/favicon.ico",
            },
            channels: ["inapp", "push"],
        })
    })

    server.post("/notifications/trigger", async (req, reply) => {
        try {
            const body = req.body as any
            const tenantId = body.tenantId || req.user.tenant_id
            const res = await svc.trigger({
                ...body,
                tenantId: requireTenant(tenantId),
            })
            reply.send(res)
        } catch (err: any) {
            server.log.error(err)
            reply.code(500).send({ error: err.message })
        }
    })
}

function previewToken(token: string) {
    if (token.length <= 14) return token
    return `${token.slice(0, 6)}...${token.slice(-6)}`
}
