import stringify from "json-stable-stringify";
import objectHash from "object-hash";
import { guilds, matches, tpMessages } from "~/drizzle/schema";
import { ExtractDataTypes, GetColumns } from "~/types/db";
import { components } from "~/types/liljudd";

export const buildMatches = (
  queryMatches: ExtractDataTypes<GetColumns<typeof matches>>[],
): components["schemas"]["match"][] =>
  queryMatches.map(
    ({
      channelId,
      createrId,
      roleId,
      messageId,
      matchType,
      opponentName,
      utc_ts,
    }) => ({
      channelId: channelId.toString(),
      createrId: createrId.toString(),
      roleId: roleId.toString(),
      messageId: messageId.toString(),
      matchType,
      opponentName,
      utc_ts: utc_ts.toISOString(),
    }),
  );

export const splitInterval = (tpInterval: number) => {
  const targetMinute = tpInterval & 63;
  const targetHour = (tpInterval >> 6) & 31;
  const targetDay = (tpInterval >> 11) & 7;

  return { targetMinute, targetHour, targetDay };
};

export const combineInterval = (
  targetMinute: number,
  targetHour: number,
  targetDay: number,
) => {
  const tpInterval = targetMinute | (targetHour << 6) | (targetDay << 11);

  return tpInterval;
};

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

export const buildTpMessages = (
  messages: ExtractDataTypes<GetColumns<typeof tpMessages>>[],
) =>
  messages.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,
  );

export function buildConfig(
  guildQuery: ExtractDataTypes<GetColumns<typeof guilds>> & {
    tpMessages: ExtractDataTypes<GetColumns<typeof tpMessages>>[];
    matches: ExtractDataTypes<GetColumns<typeof matches>>[];
  },
): components["schemas"]["guildConfig"] {
  const {
    id,
    timezone,
    tpChannelId,
    tpInterval,
    tpRolesEnabled,
    isAvailableRoleId,
    wantsToBeNotifieRoledId,
    tpMessages,
  } = guildQuery;

  const payload = {
    guildId: id.toString(),
    timezone,
    features: {
      timePlanning: {
        channelId: tpChannelId?.toString() ?? null,
        ...splitInterval(tpInterval),
        roles: {
          enabled: tpRolesEnabled,
          isAvailableRoleId: isAvailableRoleId?.toString() ?? null,
          wantsToBeNotifieRoledId: wantsToBeNotifieRoledId?.toString() ?? null,
        },
        messageIds: buildTpMessages(tpMessages),
      },
    },
    matches: buildMatches(guildQuery.matches),
  };

  // generate checksum from payload because
  // from guildQuery results in bigint serialization error
  return { ...payload, checksum: objectHash(stringify(payload)) };
}
