import { TRPCError } from "@trpc/server";
import { findUniqueOrThrow } from "../db/helpers.js";
import {
  type HolidayCalendarDb,
} from "./holiday-calendar-shared.js";
import type { HolidayCalendarScopeInput } from "@capakraken/shared";

type HolidayCalendarScope = HolidayCalendarScopeInput;

const HOLIDAY_SCOPE = {
  COUNTRY: "COUNTRY",
  STATE: "STATE",
  CITY: "CITY",
} as const satisfies Record<HolidayCalendarScope, HolidayCalendarScope>;

export function clampHolidayCalendarDate(date: Date): Date {
  const value = new Date(date);
  value.setUTCHours(0, 0, 0, 0);
  return value;
}

export function normalizeHolidayCalendarStateCode(
  stateCode: string | null | undefined,
): string | null {
  return stateCode?.trim().toUpperCase() ?? null;
}

export function normalizeHolidayCalendarScopeInput(input: {
  stateCode?: string | null | undefined;
  metroCityId?: string | null | undefined;
}) {
  return {
    stateCode: normalizeHolidayCalendarStateCode(input.stateCode),
    metroCityId: input.metroCityId ?? null,
  };
}

export function resolveHolidayCalendarUpdateScope(input: {
  existing: {
    stateCode: string | null;
    metroCityId: string | null;
  };
  data: {
    stateCode?: string | null | undefined;
    metroCityId?: string | null | undefined;
  };
}) {
  return {
    stateCode: input.data.stateCode === undefined
      ? input.existing.stateCode
      : normalizeHolidayCalendarStateCode(input.data.stateCode),
    metroCityId: input.data.metroCityId === undefined
      ? input.existing.metroCityId
      : input.data.metroCityId ?? null,
  };
}

export async function assertHolidayCalendarEntryDateAvailable(
  db: HolidayCalendarDb,
  input: {
    holidayCalendarId: string;
    date: Date;
  },
  ignoreId?: string,
) {
  const existing = await db.holidayCalendarEntry.findFirst({
    where: {
      holidayCalendarId: input.holidayCalendarId,
      date: clampHolidayCalendarDate(input.date),
      ...(ignoreId ? { id: { not: ignoreId } } : {}),
    },
    select: { id: true },
  });

  if (existing) {
    throw new TRPCError({
      code: "CONFLICT",
      message: "A holiday entry for this calendar and date already exists",
    });
  }
}

export async function assertHolidayCalendarScopeConsistency(
  db: HolidayCalendarDb,
  input: {
    scopeType: HolidayCalendarScope;
    countryId: string;
    stateCode?: string | null;
    metroCityId?: string | null;
  },
  ignoreId?: string,
) {
  if (input.scopeType === HOLIDAY_SCOPE.COUNTRY) {
    if (input.stateCode || input.metroCityId) {
      throw new TRPCError({
        code: "BAD_REQUEST",
        message: "Country calendars may not define a state or metro city",
      });
    }
  }

  if (input.scopeType === HOLIDAY_SCOPE.STATE) {
    if (!input.stateCode) {
      throw new TRPCError({
        code: "BAD_REQUEST",
        message: "State calendars require a state code",
      });
    }
    if (input.metroCityId) {
      throw new TRPCError({
        code: "BAD_REQUEST",
        message: "State calendars may not define a metro city",
      });
    }
  }

  if (input.scopeType === HOLIDAY_SCOPE.CITY) {
    if (!input.metroCityId) {
      throw new TRPCError({
        code: "BAD_REQUEST",
        message: "City calendars require a metro city",
      });
    }

    const metroCity = await findUniqueOrThrow(
      db.metroCity.findUnique({
        where: { id: input.metroCityId },
        select: { id: true, countryId: true },
      }),
      "Metro city",
    );

    if (metroCity.countryId !== input.countryId) {
      throw new TRPCError({
        code: "BAD_REQUEST",
        message: "Metro city must belong to the selected country",
      });
    }
  }

  const existing = await db.holidayCalendar.findFirst({
    where: {
      countryId: input.countryId,
      scopeType: input.scopeType,
      ...(input.scopeType === HOLIDAY_SCOPE.STATE ? { stateCode: input.stateCode ?? null } : {}),
      ...(input.scopeType === HOLIDAY_SCOPE.CITY ? { metroCityId: input.metroCityId ?? null } : {}),
      ...(ignoreId ? { id: { not: ignoreId } } : {}),
    },
    select: { id: true },
  });

  if (existing) {
    throw new TRPCError({
      code: "CONFLICT",
      message: "A holiday calendar for this exact scope already exists",
    });
  }
}
