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

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

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");

  return Res("OK", {
    channelId: guild.tpChannelId?.toString() ?? null,
    ...splitInterval(guild.tpInterval),
    roles: {
      enabled: guild.tpRolesEnabled,
      isAvailableRoleId: guild.isAvailableRoleId?.toString() ?? null,
      wantsToBeNotifieRoledId:
        guild.wantsToBeNotifieRoledId?.toString() ?? null,
    },
    messageIds: buildTpMessages(guild.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 { channelId, roles, messageIds } = body;
  if (guild.tpChannelId !== channelId)
    await db
      .update(guilds)
      .set({
        tpChannelId: channelId ? BigInt(channelId) : null,
        tpRolesEnabled: roles.enabled,
        isAvailableRoleId: roles.isAvailableRoleId
          ? BigInt(roles.isAvailableRoleId)
          : null,
        wantsToBeNotifieRoledId: roles.wantsToBeNotifieRoledId
          ? BigInt(roles.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);
};
