import { timingSafeEqual } from "node:crypto";

import { NextResponse } from "next/server";

import { prisma } from "@/lib/prisma";
import {
  ALERT_MAIL_GROUPS,
  isAlertMailGroup,
  type AlertMailGroup,
} from "@/services/alertes-mail.service";
import { sendAnonymizedAlertTestMail } from "@/services/mail-test.service";

export const dynamic = "force-dynamic";

const SCHEDULE_MODES = ["test-5-min", "weekdays-7-paris"] as const;

type ScheduleMode = (typeof SCHEDULE_MODES)[number];

function noStoreHeaders() {
  return {
    "Cache-Control": "no-store",
  };
}

function isScheduleMode(value: string): value is ScheduleMode {
  return SCHEDULE_MODES.includes(value as ScheduleMode);
}

function secretsMatch(providedSecret: string, expectedSecret: string) {
  const providedBuffer = Buffer.from(providedSecret);
  const expectedBuffer = Buffer.from(expectedSecret);

  if (providedBuffer.length !== expectedBuffer.length) {
    return false;
  }

  return timingSafeEqual(providedBuffer, expectedBuffer);
}

function isSchedulerAuthorized(request: Request) {
  const expectedSecret = process.env.ALERT_MAIL_CRON_SECRET?.trim() ?? "";
  const providedSecret = request.headers.get("x-cron-secret")?.trim() ?? "";

  if (expectedSecret.length < 32 || providedSecret.length === 0) {
    return false;
  }

  return secretsMatch(providedSecret, expectedSecret);
}

function getParisDateKey(date: Date) {
  const parts = new Intl.DateTimeFormat("fr-FR", {
    timeZone: "Europe/Paris",
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).formatToParts(date);

  const year = parts.find((part) => part.type === "year")?.value;
  const month = parts.find((part) => part.type === "month")?.value;
  const day = parts.find((part) => part.type === "day")?.value;

  if (!year || !month || !day) {
    throw new Error("Impossible de calculer la date Europe/Paris");
  }

  return `${year}-${month}-${day}`;
}

function getExecutionKey(
  mode: ScheduleMode,
  group: AlertMailGroup,
  now: Date,
) {
  if (mode === "test-5-min") {
    const fiveMinuteBucket = Math.floor(now.getTime() / (5 * 60 * 1_000));
    return `${mode}:${group}:${fiveMinuteBucket}`;
  }

  return `${mode}:${group}:${getParisDateKey(now)}`;
}

function readStringProperty(
  body: object,
  property: "groupe" | "mode",
) {
  const value = (body as Record<string, unknown>)[property];
  return typeof value === "string"
    ? value.trim().toLocaleLowerCase("fr")
    : "";
}

export async function POST(request: Request) {
  if (!isSchedulerAuthorized(request)) {
    return NextResponse.json(
      { message: "Planificateur non autorisé" },
      { status: 401, headers: noStoreHeaders() },
    );
  }

  let body: unknown;

  try {
    body = await request.json();
  } catch {
    return NextResponse.json(
      { message: "Corps JSON invalide" },
      { status: 400, headers: noStoreHeaders() },
    );
  }

  if (typeof body !== "object" || body === null) {
    return NextResponse.json(
      { message: "Corps JSON invalide" },
      { status: 400, headers: noStoreHeaders() },
    );
  }

  const requestedGroup = readStringProperty(body, "groupe");
  const requestedMode = readStringProperty(body, "mode");

  if (!isAlertMailGroup(requestedGroup)) {
    return NextResponse.json(
      {
        message: "Groupe destinataire invalide",
        allowedGroups: ALERT_MAIL_GROUPS,
      },
      { status: 400, headers: noStoreHeaders() },
    );
  }

  if (!isScheduleMode(requestedMode)) {
    return NextResponse.json(
      {
        message: "Mode de planification invalide",
        allowedModes: SCHEDULE_MODES,
      },
      { status: 400, headers: noStoreHeaders() },
    );
  }

  const now = new Date();
  const executionKey = getExecutionKey(requestedMode, requestedGroup, now);

  try {
    const insertedRows = await prisma.$executeRaw`
      INSERT IGNORE INTO alert_mail_executions
        (execution_key, mode, groupe, statut, started_at)
      VALUES
        (${executionKey}, ${requestedMode}, ${requestedGroup}, 'running', NOW())
    `;

    if (insertedRows === 0) {
      const reclaimedRows = await prisma.$executeRaw`
        UPDATE alert_mail_executions
        SET
          statut = 'running',
          error_message = NULL,
          started_at = NOW(),
          finished_at = NULL
        WHERE
          execution_key = ${executionKey}
          AND statut = 'failed'
      `;

      if (reclaimedRows === 0) {
        return NextResponse.json(
          {
            message: "Cet envoi planifié a déjà été traité.",
            item: {
              executionKey,
              skipped: true,
            },
          },
          { headers: noStoreHeaders() },
        );
      }
    }
  } catch (error) {
    console.error(
      "Impossible de réserver l'exécution du mail planifié :",
      error,
    );

    return NextResponse.json(
      {
        message:
          "La journalisation des mails planifiés est indisponible. Aucun mail n'a été envoyé.",
      },
      { status: 500, headers: noStoreHeaders() },
    );
  }

  try {
    const result = await sendAnonymizedAlertTestMail(requestedGroup);

    await prisma.$executeRaw`
      UPDATE alert_mail_executions
      SET
        statut = 'sent',
        recipient = ${result.recipient},
        message_id = ${result.messageId},
        finished_at = NOW()
      WHERE execution_key = ${executionKey}
    `;

    return NextResponse.json(
      {
        message: `Mail planifié anonymisé envoyé à ${result.recipient}.`,
        item: {
          ...result,
          executionKey,
          mode: requestedMode,
          skipped: false,
        },
      },
      { headers: noStoreHeaders() },
    );
  } catch (error) {
    console.error("Échec de l'envoi du mail planifié anonymisé :", error);

    try {
      await prisma.$executeRaw`
        UPDATE alert_mail_executions
        SET
          statut = 'failed',
          error_message = 'Échec de l''envoi SMTP de test',
          finished_at = NOW()
        WHERE execution_key = ${executionKey}
      `;
    } catch (logError) {
      console.error(
        "Impossible de journaliser l'échec du mail planifié :",
        logError,
      );
    }

    return NextResponse.json(
      {
        message:
          "Échec de l'envoi planifié. Vérifie la configuration Gmail et les journaux du backend.",
      },
      { status: 502, headers: noStoreHeaders() },
    );
  }
}
