import { PrismaClient, type HolidayCalendarEntry } from "@prisma/client";
import { buildHolidayCalendarSeedDefinitions } from "./holiday-calendar-seed-data.js";
import { loadWorkspaceEnv } from "./load-workspace-env.js";
import { assertCapaKrakenDbTarget } from "./safe-destructive-env.js";

loadWorkspaceEnv();

const prisma = new PrismaClient();

const YEARS = [2026, 2027];
const SEED_SOURCE = "seed:holiday-calendars:2026-2027";

type ExistingCalendar = {
  id: string;
  entries: Pick<HolidayCalendarEntry, "id" | "date" | "source">[];
};

function toUtcDate(isoDate: string): Date {
  return new Date(`${isoDate}T00:00:00.000Z`);
}

function dateKey(value: Date): string {
  return value.toISOString().slice(0, 10);
}

async function findScopedCalendar(input: {
  countryId: string;
  scopeType: "COUNTRY" | "STATE" | "CITY";
  stateCode?: string | undefined;
  metroCityId?: string | undefined;
}): Promise<ExistingCalendar | null> {
  return prisma.holidayCalendar.findFirst({
    where: {
      countryId: input.countryId,
      scopeType: input.scopeType,
      stateCode: input.scopeType === "STATE" ? input.stateCode ?? null : null,
      metroCityId: input.scopeType === "CITY" ? input.metroCityId ?? null : null,
    },
    select: {
      id: true,
      entries: {
        select: {
          id: true,
          date: true,
          source: true,
        },
      },
    },
  });
}

async function main() {
  assertCapaKrakenDbTarget("db:seed:holidays");
  console.log("Seeding holiday calendars for 2026-2027...");

  const countries = await prisma.country.findMany({
    select: {
      id: true,
      code: true,
      name: true,
      metroCities: {
        select: {
          id: true,
          name: true,
        },
      },
    },
    orderBy: { code: "asc" },
  });

  const activeGermanStatesRows = await prisma.resource.findMany({
    where: {
      isActive: true,
      country: { code: "DE" },
      federalState: { not: null },
    },
    select: { federalState: true },
    distinct: ["federalState"],
  });

  const definitions = buildHolidayCalendarSeedDefinitions({
    availableCountryCodes: countries.map((country) => country.code),
    availableCitiesByCountry: Object.fromEntries(
      countries.map((country) => [
        country.code,
        country.metroCities.map((city) => city.name),
      ]),
    ),
    activeGermanStates: activeGermanStatesRows
      .map((row) => row.federalState)
      .filter((stateCode): stateCode is string => Boolean(stateCode)),
    years: YEARS,
  });

  const countryByCode = new Map(countries.map((country) => [country.code, country]));
  const cityByCountryAndName = new Map(
    countries.flatMap((country) =>
      country.metroCities.map((city) => [`${country.code}:${city.name}`, city] as const),
    ),
  );

  let createdCalendars = 0;
  let reusedCalendars = 0;
  let createdEntries = 0;
  let updatedEntries = 0;
  let skippedManualEntries = 0;

  for (const definition of definitions) {
    const country = countryByCode.get(definition.countryCode);
    if (!country) {
      continue;
    }

    const metroCity = definition.cityName
      ? cityByCountryAndName.get(`${definition.countryCode}:${definition.cityName}`)
      : null;

    if (definition.scopeType === "CITY" && !metroCity) {
      console.warn(
        `Skipping city calendar ${definition.name}: city ${definition.cityName ?? "?"} not found.`,
      );
      continue;
    }

    let calendar = await findScopedCalendar({
      countryId: country.id,
      scopeType: definition.scopeType,
      stateCode: definition.stateCode,
      metroCityId: metroCity?.id,
    });

    if (!calendar) {
      calendar = await prisma.holidayCalendar.create({
        data: {
          name: definition.name,
          scopeType: definition.scopeType,
          countryId: country.id,
          stateCode: definition.scopeType === "STATE" ? definition.stateCode ?? null : null,
          metroCityId: definition.scopeType === "CITY" ? metroCity?.id ?? null : null,
          priority: definition.priority,
          isActive: true,
        },
        select: {
          id: true,
          entries: {
            select: {
              id: true,
              date: true,
              source: true,
            },
          },
        },
      });
      createdCalendars += 1;
    } else {
      reusedCalendars += 1;
    }

    const entriesByDate = new Map(calendar.entries.map((entry) => [dateKey(entry.date), entry]));

    for (const entry of definition.entries) {
      const existingEntry = entriesByDate.get(entry.date);

      if (!existingEntry) {
        await prisma.holidayCalendarEntry.create({
          data: {
            holidayCalendarId: calendar.id,
            date: toUtcDate(entry.date),
            name: entry.name,
            isRecurringAnnual: entry.isRecurringAnnual,
            source: SEED_SOURCE,
          },
        });
        createdEntries += 1;
        continue;
      }

      if (existingEntry.source && existingEntry.source !== SEED_SOURCE) {
        skippedManualEntries += 1;
        continue;
      }

      await prisma.holidayCalendarEntry.update({
        where: { id: existingEntry.id },
        data: {
          name: entry.name,
          isRecurringAnnual: entry.isRecurringAnnual,
          source: SEED_SOURCE,
        },
      });
      updatedEntries += 1;
    }
  }

  console.log(`  calendars created: ${createdCalendars}`);
  console.log(`  calendars reused: ${reusedCalendars}`);
  console.log(`  entries created: ${createdEntries}`);
  console.log(`  entries updated: ${updatedEntries}`);
  console.log(`  manual entries preserved: ${skippedManualEntries}`);
}

main()
  .catch((error) => {
    console.error(error);
    process.exit(1);
  })
  .finally(() => prisma.$disconnect());
