import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { adminProcedure } from "../trpc.js";
import { asHolidayCalendarDb, type HolidayReadContext } from "./holiday-calendar-shared.js";
import {
  holidayCalendarDetailInclude,
  holidayCalendarListInclude,
} from "./holiday-calendar-support.js";

const HolidayCalendarCatalogInputSchema = z.object({
  includeInactive: z.boolean().optional(),
  countryCode: z.string().trim().min(1).optional(),
  scopeType: z.enum(["COUNTRY", "STATE", "CITY"]).optional(),
  stateCode: z.string().trim().min(1).optional(),
  metroCity: z.string().trim().min(1).optional(),
}).optional();

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

function formatHolidayCalendarEntryDetail(entry: {
  id: string;
  date: Date;
  name: string;
  isRecurringAnnual?: boolean | null;
  source?: string | null;
}) {
  return {
    id: entry.id,
    date: formatIsoDate(entry.date),
    name: entry.name,
    isRecurringAnnual: entry.isRecurringAnnual ?? false,
    source: entry.source ?? null,
  };
}

function formatHolidayCalendarDetail(calendar: {
  id: string;
  name: string;
  scopeType: string;
  stateCode?: string | null;
  isActive?: boolean | null;
  priority?: number | null;
  country?: { id: string; code: string; name: string } | null;
  metroCity?: { id: string; name: string } | null;
  _count?: { entries?: number | null } | null;
  entries?: Array<{
    id: string;
    date: Date;
    name: string;
    isRecurringAnnual?: boolean | null;
    source?: string | null;
  }> | null;
}) {
  const entries = calendar.entries?.map(formatHolidayCalendarEntryDetail) ?? [];

  return {
    id: calendar.id,
    name: calendar.name,
    scopeType: calendar.scopeType,
    stateCode: calendar.stateCode ?? null,
    isActive: calendar.isActive ?? true,
    priority: calendar.priority ?? 0,
    country: calendar.country
      ? {
          id: calendar.country.id,
          code: calendar.country.code,
          name: calendar.country.name,
        }
      : null,
    metroCity: calendar.metroCity
      ? {
          id: calendar.metroCity.id,
          name: calendar.metroCity.name,
        }
      : null,
    entryCount: calendar._count?.entries ?? entries.length,
    entries,
  };
}

async function readCalendarsSnapshot(
  ctx: HolidayReadContext,
  input?: z.infer<typeof HolidayCalendarCatalogInputSchema>,
) {
  const db = asHolidayCalendarDb(ctx.db);
  const where = {
    ...(input?.includeInactive ? {} : { isActive: true }),
    ...(input?.countryCode
      ? {
          country: { code: { equals: input.countryCode.trim().toUpperCase(), mode: "insensitive" as const } },
        }
      : {}),
    ...(input?.scopeType ? { scopeType: input.scopeType } : {}),
    ...(input?.stateCode ? { stateCode: input.stateCode.trim().toUpperCase() } : {}),
    ...(input?.metroCity
      ? {
          metroCity: { name: { contains: input.metroCity.trim(), mode: "insensitive" as const } },
        }
      : {}),
  };

  return db.holidayCalendar.findMany({
    where,
    include: holidayCalendarListInclude,
    orderBy: [
      { country: { name: "asc" } },
      { scopeType: "asc" },
      { priority: "desc" },
      { name: "asc" },
    ],
  });
}

async function readCalendarByIdentifierSnapshot(ctx: HolidayReadContext, identifier: string) {
  const db = asHolidayCalendarDb(ctx.db);
  const trimmedIdentifier = identifier.trim();

  let calendar = await db.holidayCalendar.findUnique({
    where: { id: trimmedIdentifier },
    include: holidayCalendarDetailInclude,
  });

  if (!calendar) {
    calendar = await db.holidayCalendar.findFirst({
      where: { name: { equals: trimmedIdentifier, mode: "insensitive" } },
      include: holidayCalendarDetailInclude,
    });
  }

  if (!calendar) {
    calendar = await db.holidayCalendar.findFirst({
      where: { name: { contains: trimmedIdentifier, mode: "insensitive" } },
      include: holidayCalendarDetailInclude,
    });
  }

  if (!calendar) {
    throw new TRPCError({ code: "NOT_FOUND", message: `Holiday calendar not found: ${trimmedIdentifier}` });
  }

  return calendar;
}

export const holidayCalendarCatalogReadProcedures = {
  listCalendars: adminProcedure
    .input(HolidayCalendarCatalogInputSchema)
    .query(async ({ ctx, input }) => readCalendarsSnapshot(ctx, input)),

  listCalendarsDetail: adminProcedure
    .input(HolidayCalendarCatalogInputSchema)
    .query(async ({ ctx, input }) => {
      const calendars = await readCalendarsSnapshot(ctx, input);
      return {
        count: calendars.length,
        calendars: calendars.map(formatHolidayCalendarDetail),
      };
    }),

  getCalendarByIdentifier: adminProcedure
    .input(z.object({ identifier: z.string().trim().min(1) }))
    .query(async ({ ctx, input }) => readCalendarByIdentifierSnapshot(ctx, input.identifier)),

  getCalendarByIdentifierDetail: adminProcedure
    .input(z.object({ identifier: z.string().trim().min(1) }))
    .query(async ({ ctx, input }) => {
      const calendar = await readCalendarByIdentifierSnapshot(ctx, input.identifier);
      return formatHolidayCalendarDetail(calendar);
    }),

  getCalendarById: adminProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const db = asHolidayCalendarDb(ctx.db);

      return findUniqueOrThrow(
        db.holidayCalendar.findUnique({
          where: { id: input.id },
          include: holidayCalendarDetailInclude,
        }),
        "Holiday calendar",
      );
    }),
};
