import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import type { RouteContext } from "./types.js";
import { fetchDailyProgress } from "../server/data-fetchers.js";
import { recordAudit } from "../server/admin-service.js";

const paramsUserIdSchema = {
  type: "object" as const,
  required: ["userId"],
  properties: { userId: { type: "string", pattern: "^[0-9]+$" } },
};

export const registerDailyRoutes = (
  app: FastifyInstance,
  ctx: RouteContext,
) => {
  const { db, requireAuth } = ctx;

  // GET /daily/:userId
  app.get(
    "/daily/:userId",
    {
      preHandler: requireAuth,
      schema: { params: paramsUserIdSchema },
    },
    async (request, reply) => {
      const userId = Number((request.params as { userId: string }).userId);
      const tokenUserId = request.user?.sub;
      if (!Number.isFinite(userId)) {
        reply.code(400);
        return { error: "userId invalide" };
      }
      if (!tokenUserId || tokenUserId !== userId) {
        reply.code(403);
        return { error: "Acces refuse" };
      }
      const progress = await fetchDailyProgress(db, userId);
      return { progress };
    },
  );

  // POST /daily/:userId/complete
  app.post(
    "/daily/:userId/complete",
    {
      preHandler: requireAuth,
      config: {
        rateLimit: {
          max: 30,
          timeWindow: "1 minute",
        },
      },
      schema: {
        params: paramsUserIdSchema,
        body: {
          type: "object",
          required: ["dateKey"],
          additionalProperties: false,
          properties: {
            dateKey: { type: "string", minLength: 8, maxLength: 16 },
          },
        },
      },
    },
    async (request, reply) => {
      const userId = Number((request.params as { userId: string }).userId);
      const body = request.body as { dateKey?: string } | undefined;
      const tokenUserId = request.user?.sub;
      const dateKey = body?.dateKey?.trim();
      if (!Number.isFinite(userId)) {
        reply.code(400);
        return { error: "userId invalide" };
      }
      if (!tokenUserId || tokenUserId !== userId) {
        reply.code(403);
        return { error: "Acces refuse" };
      }
      if (!dateKey) {
        reply.code(400);
        return { error: "Payload invalide" };
      }
      const current = await fetchDailyProgress(db, userId);
      if (!current.completed.includes(dateKey)) {
        const next = [...current.completed, dateKey];
        await db.execute(
          "UPDATE daily_progress SET completed = ? WHERE user_id = ?",
          [JSON.stringify(next), userId],
        );
      }
      await recordAudit(db, {
        userId,
        action: "daily.complete",
        meta: { dateKey },
        ip: request.ip,
        userAgent: request.headers["user-agent"]?.toString() ?? null,
        installationId:
          request.headers["x-installation-id"]?.toString() ?? null,
      });
      const progress = await fetchDailyProgress(db, userId);
      return { progress };
    },
  );

  // POST /daily/:userId/claim
  app.post(
    "/daily/:userId/claim",
    {
      preHandler: requireAuth,
      config: {
        rateLimit: {
          max: 10,
          timeWindow: "1 minute",
        },
      },
      schema: {
        params: paramsUserIdSchema,
        body: {
          type: "object",
          required: ["monthKey"],
          additionalProperties: false,
          properties: {
            monthKey: { type: "string", minLength: 6, maxLength: 8 },
          },
        },
      },
    },
    async (request, reply) => {
      const userId = Number((request.params as { userId: string }).userId);
      const body = request.body as { monthKey?: string } | undefined;
      const tokenUserId = request.user?.sub;
      const monthKey = body?.monthKey?.trim();
      if (!Number.isFinite(userId)) {
        reply.code(400);
        return { error: "userId invalide" };
      }
      if (!tokenUserId || tokenUserId !== userId) {
        reply.code(403);
        return { error: "Acces refuse" };
      }
      if (!monthKey) {
        reply.code(400);
        return { error: "Payload invalide" };
      }
      const current = await fetchDailyProgress(db, userId);
      if (!current.monthlyClaims.includes(monthKey)) {
        const next = [...current.monthlyClaims, monthKey];
        await db.execute(
          "UPDATE daily_progress SET monthly_claims = ? WHERE user_id = ?",
          [JSON.stringify(next), userId],
        );
      }
      await recordAudit(db, {
        userId,
        action: "daily.claim",
        meta: { monthKey },
        ip: request.ip,
        userAgent: request.headers["user-agent"]?.toString() ?? null,
        installationId:
          request.headers["x-installation-id"]?.toString() ?? null,
      });
      const progress = await fetchDailyProgress(db, userId);
      return { progress };
    },
  );
};
