import { format } from "date-fns";
import { enUS, km } from "date-fns/locale";

import { formatNumber, toKhmerDigits } from "@/lib/number-utils";

export type CalendarLanguage = "en" | "kh" | "km";
export type CalendarWeekStartsOn = 0 | 1;

export type CalendarMonth = {
  year: number;
  monthIndex: number;
};

export type CalendarDay = {
  dateKey: string;
  dayNumber: number;
  isCurrentMonth: boolean;
};

const CAMBODIA_TIME_ZONE = "Asia/Phnom_Penh";

function isKhmerLanguage(language: CalendarLanguage) {
  return language === "kh" || language === "km";
}

function getDateLocale(language: CalendarLanguage) {
  return isKhmerLanguage(language) ? km : enUS;
}

function toDateKey(date: Date) {
  const year = date.getUTCFullYear();
  const month = String(date.getUTCMonth() + 1).padStart(2, "0");
  const day = String(date.getUTCDate()).padStart(2, "0");

  return `${year}-${month}-${day}`;
}

function parseDateKey(value?: string | null) {
  if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return null;

  const [year, month, day] = value.split("-").map(Number);
  const date = new Date(Date.UTC(year, month - 1, day));

  return toDateKey(date) === value ? date : null;
}

export function buildCalendarMonth(
  year: number,
  monthIndex: number,
  weekStartsOn: CalendarWeekStartsOn,
): CalendarDay[] {
  const firstDay = new Date(Date.UTC(year, monthIndex, 1));
  const lastDay = new Date(Date.UTC(year, monthIndex + 1, 0));
  const leadingDays =
    (firstDay.getUTCDay() - weekStartsOn + 7) % 7;
  const totalDays = leadingDays + lastDay.getUTCDate();
  const cellCount = Math.ceil(totalDays / 7) * 7;

  return Array.from({ length: cellCount }, (_, index) => {
    const date = new Date(
      Date.UTC(year, monthIndex, index - leadingDays + 1),
    );

    return {
      dateKey: toDateKey(date),
      dayNumber: date.getUTCDate(),
      isCurrentMonth: date.getUTCMonth() === monthIndex,
    };
  });
}

export function getCalendarMonthLabel(
  month: CalendarMonth,
  language: CalendarLanguage,
) {
  const date = new Date(month.year, month.monthIndex, 1);

  return format(date, "MMMM", { locale: getDateLocale(language) });
}

export function getCalendarWeekdayLabels(
  weekStartsOn: CalendarWeekStartsOn,
  language: CalendarLanguage,
) {
  const sunday = new Date(2026, 0, 4);
  const formatPattern = isKhmerLanguage(language) ? "EEEE" : "EEE";

  return Array.from({ length: 7 }, (_, index) => {
    const dayOffset = (weekStartsOn + index) % 7;
    const date = new Date(
      sunday.getFullYear(),
      sunday.getMonth(),
      sunday.getDate() + dayOffset,
    );

    return format(date, formatPattern, { locale: getDateLocale(language) });
  });
}

export function formatCalendarDate(
  value?: string | null,
  language: CalendarLanguage = "en",
) {
  const date = parseDateKey(value);
  if (!date) return "-";

  const localDate = new Date(
    date.getUTCFullYear(),
    date.getUTCMonth(),
    date.getUTCDate(),
  );
  const formattedDate = format(localDate, "dd MMMM yyyy", {
    locale: getDateLocale(language),
  });

  return isKhmerLanguage(language)
    ? toKhmerDigits(formattedDate)
    : formattedDate;
}

export function formatCalendarTimeText(
  value?: string | null,
  language: CalendarLanguage = "en",
) {
  const timeText = value?.trim();
  if (!timeText) return "-";

  const timeParts = timeText.split(/\s+-\s+/);
  const formattedParts = timeParts.map((part) => {
    const match = /^(\d{1,2}):(\d{2})(?::(\d{2}))?(?:\s+(AM|PM))?$/i.exec(
      part,
    );

    const dateTime = !match && /T\d{2}:\d{2}/.test(part)
      ? new Date(part)
      : null;
    if (!match && (!dateTime || Number.isNaN(dateTime.getTime()))) {
      return null;
    }

    const hour = match ? Number(match[1]) : dateTime!.getUTCHours();
    const minute = match ? Number(match[2]) : dateTime!.getUTCMinutes();
    const second = match ? Number(match[3] ?? 0) : dateTime!.getUTCSeconds();
    const suppliedDayPeriod = match?.[4]?.toUpperCase();
    const hasDayPeriod = Boolean(suppliedDayPeriod);

    if (
      minute > 59 ||
      second > 59 ||
      (hasDayPeriod ? hour < 1 || hour > 12 : hour > 23)
    ) {
      return null;
    }

    const dayPeriod = suppliedDayPeriod ?? (hour >= 12 ? "PM" : "AM");
    const twelveHour = hasDayPeriod ? hour : hour % 12 || 12;

    return `${twelveHour}:${String(minute).padStart(2, "0")} ${dayPeriod}`;
  });

  if (formattedParts.some((part) => part === null)) return "-";

  const formattedTime = formattedParts.join(" - ");
  if (!isKhmerLanguage(language)) return formattedTime;

  const morningLabel = format(new Date(2026, 0, 1, 9), "a", { locale: km });
  const afternoonLabel = format(new Date(2026, 0, 1, 15), "a", {
    locale: km,
  });
  const localizedTime = formattedTime
    .replace(/\bAM\b/gi, morningLabel)
    .replace(/\bPM\b/gi, afternoonLabel);

  return toKhmerDigits(localizedTime);
}

export function formatCalendarTimeRange(
  startTime?: string | null,
  endTime?: string | null,
  language: CalendarLanguage = "en",
) {
  if (!startTime || !endTime) return "-";

  return formatCalendarTimeText(`${startTime} - ${endTime}`, language);
}

export function getCalendarTodayDateKey() {
  const parts = new Intl.DateTimeFormat("en-US", {
    timeZone: CAMBODIA_TIME_ZONE,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).formatToParts(new Date());

  const year = parts.find((part) => part.type === "year")?.value;
  const month = parts.find((part) => part.type === "month")?.value;
  const day = parts.find((part) => part.type === "day")?.value;

  return `${year}-${month}-${day}`;
}

export function getCalendarLabels(language: CalendarLanguage) {
  if (isKhmerLanguage(language)) {
    return {
      participant: "អ្នកចូលរួម",
      previousMonth: "បង្ហាញខែមុន",
      nextMonth: "បង្ហាញខែបន្ទាប់",
    };
  }

  return {
    participant: "Participant",
    previousMonth: "Show previous month",
    nextMonth: "Show next month",
  };
}

export function formatCalendarParticipantText(
  count?: string | number | null,
  language: CalendarLanguage = "en",
) {
  const formattedCount = formatCalendarNumber(count, language);

  if (isKhmerLanguage(language)) {
    return `អ្នកចូលរួម៖ ${formattedCount}`;
  }

  return `Participant: ${formattedCount}`;
}

export function formatCalendarNumber(
  value?: string | number | null,
  language: CalendarLanguage = "en",
) {
  return formatNumber(value, language);
}
