import { APIEvent } from "@solidjs/start/server/types";
import { and, eq } from "drizzle-orm";
import db from "~/drizzle";
import { guilds, tpMessages } from "~/drizzle/schema";
import { BasicAuth } from "~/lib/auth";
import { ErrorResponse, Res } from "~/lib/responses";
import { zodBigIntId, zodTpMessages } from "~/lib/zod";
import { APIResponse, RequestBody } from "~/types/backend";

type Path = "/api/{guildId}/timePlanning";

const DayKeys = ["0", "1", "2", "3", "4", "5", "6"] as const;
type DayKeys = (typeof DayKeys)[number];
type Messages = Record<DayKeys, string | null>;

export const GET = async (
  event: APIEvent,
): Promise<APIResponse<Path, "get">> => {
  switch (event.request.headers.get("authorization")) {
    case BasicAuth.unencoded:
    case BasicAuth.encoded:
      break;

    default:
      return ErrorResponse("UNAUTHORIZED");
  }

  let guildId: bigint;
  try {
    guildId = zodBigIntId.parse(event.params.guildId);
  } catch (e) {
    return ErrorResponse("BAD_REQUEST", JSON.stringify(e));
  }

  const guild = await db.query.guilds.findFirst({
    where: eq(guilds.id, guildId),
    with: {
      tpMessages: true,
    },
  });

  if (!guild) return ErrorResponse("NOT_FOUND");

  const tpMessages = guild.tpMessages.reduce(
    (acc, message) => {
      const day = message.day.toString() as DayKeys;
      if (!/^[0-6]$/.test(day)) return acc;
      acc[day] = message.messageId?.toString() ?? null;
      return acc;
    },
    {
      "0": null,
      "1": null,
      "2": null,
      "3": null,
      "4": null,
      "5": null,
      "6": null,
    } as Messages,
  );

  return Res("OK", {
    enabled: guild.tpEnabled,
    channelId: guild.tpChannelId?.toString() ?? null,
    rolesEnabled: guild.tpRolesEnabled,
    isAvailableRoleId: guild.isAvailableRoleId?.toString() ?? null,
    wantsToBeNotifieRoledId: guild.wantsToBeNotifieRoledId?.toString() ?? null,
    messageIds: tpMessages,
  });
};

export const PUT = async (
  event: APIEvent,
): Promise<APIResponse<Path, "put">> => {
  switch (event.request.headers.get("authorization")) {
    case BasicAuth.unencoded:
    case BasicAuth.encoded:
      break;

    default:
      return ErrorResponse("UNAUTHORIZED");
  }

  let guildId: bigint;
  try {
    guildId = zodBigIntId.parse(event.params.guildId);
  } catch (e) {
    return ErrorResponse("BAD_REQUEST", JSON.stringify(e));
  }

  const guild = await db.query.guilds
    .findFirst({
      where: eq(guilds.id, guildId),
      with: { tpMessages: true },
    })
    .execute();

  if (!guild) return ErrorResponse("NOT_FOUND");

  const unparsedBody = await new Response(event.request.body).json();

  let body: RequestBody<Path, "put">;
  try {
    body = zodTpMessages.parse(unparsedBody);
  } catch (e) {
    return ErrorResponse("BAD_REQUEST", JSON.stringify(e));
  }

  const {
    enabled,
    channelId,
    rolesEnabled,
    isAvailableRoleId,
    wantsToBeNotifieRoledId,
    messageIds,
  } = body;
  if (guild.tpChannelId !== channelId)
    await db
      .update(guilds)
      .set({
        tpEnabled: enabled,
        tpChannelId: channelId ? BigInt(channelId) : null,
        tpRolesEnabled: rolesEnabled,
        isAvailableRoleId: isAvailableRoleId ? BigInt(isAvailableRoleId) : null,
        wantsToBeNotifieRoledId: wantsToBeNotifieRoledId
          ? BigInt(wantsToBeNotifieRoledId)
          : null,
      })
      .where(eq(guilds.id, guild.id))
      .execute();

  await Promise.all(
    DayKeys.map(async (dayStr) => {
      const day = parseInt(dayStr);
      const messageId = messageIds[dayStr];
      await db
        .update(tpMessages)
        .set({ messageId: messageId ? BigInt(messageId) : null })
        .where(and(eq(tpMessages.guildId, guild.id), eq(tpMessages.day, day)))
        .execute();
    }),
  );

  return Res("NO_CONTENT", null);
};
