import { FastifyInstance } from "fastify";
import fp from "fastify-plugin";
import { secrets } from "../utils/secrets";
import { and, eq } from "drizzle-orm";
import { authUsers, m2mApiKeys } from "../../db/schema";
import { createHash } from "node:crypto";

/**
 * Fastify Plugin für Machine-to-Machine Authentifizierung.
 *
 * Dieses Plugin prüft, ob der Header `x-api-key` vorhanden ist
 * und mit dem in der .env hinterlegten M2M_API_KEY übereinstimmt.
 *
 * Verwendung:
 *   server.register(m2mAuthPlugin, { allowedPrefix: '/internal' })
 */
export default fp(async (server: FastifyInstance, opts: { allowedPrefix?: string } = {}) => {
    const hashApiKey = (apiKey: string) =>
        createHash("sha256").update(apiKey, "utf8").digest("hex")

    server.addHook("preHandler", async (req, reply) => {
        try {
            const apiKeyHeader = req.headers["x-api-key"];
            const apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;

            if (!apiKey) {
                server.log.warn(`[M2M Auth] Ungültiger oder fehlender API-Key bei ${req.url}`);
                return reply.status(401).send({ error: "Unauthorized" });
            }

            const keyHash = hashApiKey(apiKey);

            const keyRows = await server.db
                .select({
                    id: m2mApiKeys.id,
                    tenantId: m2mApiKeys.tenantId,
                    userId: m2mApiKeys.userId,
                    active: m2mApiKeys.active,
                    expiresAt: m2mApiKeys.expiresAt,
                    name: m2mApiKeys.name,
                    userEmail: authUsers.email,
                })
                .from(m2mApiKeys)
                .innerJoin(authUsers, eq(authUsers.id, m2mApiKeys.userId))
                .where(and(
                    eq(m2mApiKeys.keyHash, keyHash),
                    eq(m2mApiKeys.active, true)
                ))
                .limit(1)

            let key = keyRows[0]
            if (!key) {
                const fallbackValid = apiKey === secrets.M2M_API_KEY
                if (!fallbackValid) {
                    server.log.warn(`[M2M Auth] Ungültiger API-Key bei ${req.url}`)
                    return reply.status(401).send({ error: "Unauthorized" })
                }

                // Backward compatibility mode for one global key.
                // The caller must provide user/tenant identifiers in headers.
                const tenantIdHeader = req.headers["x-tenant-id"]
                const userIdHeader = req.headers["x-user-id"]
                const tenantId = Number(Array.isArray(tenantIdHeader) ? tenantIdHeader[0] : tenantIdHeader)
                const userId = Array.isArray(userIdHeader) ? userIdHeader[0] : userIdHeader

                if (!tenantId || !userId) {
                    return reply.status(401).send({ error: "Missing x-tenant-id or x-user-id for legacy M2M key" })
                }

                const users = await server.db
                    .select({ email: authUsers.email })
                    .from(authUsers)
                    .where(eq(authUsers.id, userId))
                    .limit(1)

                if (!users[0]) {
                    return reply.status(401).send({ error: "Unknown user for legacy M2M key" })
                }

                req.user = {
                    user_id: userId,
                    email: users[0].email,
                    tenant_id: tenantId
                }
            } else {
                if (key.expiresAt && new Date(key.expiresAt).getTime() < Date.now()) {
                    return reply.status(401).send({ error: "Expired API key" })
                }

                req.user = {
                    user_id: key.userId,
                    email: key.userEmail,
                    tenant_id: key.tenantId
                }

                await server.db
                    .update(m2mApiKeys)
                    .set({ lastUsedAt: new Date(), updatedAt: new Date() })
                    .where(eq(m2mApiKeys.id, key.id))
            }

            (req as any).m2m = {
                verified: true,
                type: "internal",
                key: apiKey,
            };

            req.role = "m2m"
            req.permissions = []
            req.hasPermission = () => false
        } catch (err) {
            // @ts-ignore
            server.log.error("[M2M Auth] Fehler beim Prüfen des API-Keys:", err);
            return reply.status(500).send({ error: "Internal Server Error" });
        }
    });
});

declare module "fastify" {
    interface FastifyRequest {
        m2m?: {
            verified: boolean;
            type: "internal";
            key: string;
        };
    }
}
