import type { Locale } from "@/lib/i18n"

/**
 * Dates, in the reader's language and the reader's calendar.
 *
 * **Thailand counts years from the Buddhist Era**, 543 ahead of the Common Era:
 * today is 2026 in English and 2569 in Thai. It is not a stylistic preference,
 * it is the year on every document in the building, and a Thai screen showing
 * 2026 is showing a year 543 out.
 *
 * `-u-ca-buddhist` is spelled out rather than left to the locale's default.
 * `th-TH` does default to it in every ICU build we have, and that is exactly the
 * kind of thing that is true until an upgrade makes it false, silently, on a
 * screen nobody re-reads.
 *
 * Pure, with nothing under it but a type, because the tables that call it are
 * client components.
 *
 * **Days and instants are formatted by different functions here**, as they are
 * everywhere else in this project. A due date is a `YYYY-MM-DD` wall-calendar
 * day and is formatted in UTC so the formatter cannot shift it; a timestamp is a
 * real moment and is formatted in Bangkok, because the factory has one clock and
 * it is not the server's.
 */

const TAGS: Record<Locale, string> = {
  en: "en-GB",
  th: "th-TH-u-ca-buddhist",
}

/**
 * The tag itself, for the callers that cannot use the functions below.
 *
 * `react-day-picker` formats its own chrome, hands back a `Date` and takes a
 * string, so a calendar left to itself prints สิงหาคม 2026 under a screen that
 * says 2569 everywhere else. Exported rather than copied, because the reason
 * `-u-ca-buddhist` is spelled out is written down once, above.
 */
export function dateTag(locale: Locale): string {
  return TAGS[locale]
}

/**
 * Thai narrows the weekday header to one syllable where English wants three.
 *
 * `narrow` in English gives S, M, T, W, T, F, S, where two pairs are the same
 * letter and the only thing telling Tuesday from Thursday is which column it
 * sits in. Thai's narrow forms are distinct.
 */
const WEEKDAY_WIDTH: Record<Locale, "short" | "narrow"> = {
  en: "short",
  th: "narrow",
}

/**
 * The four strings `react-day-picker` writes for itself.
 *
 * The library reaches for date-fns for these, and date-fns's Thai does **not**
 * use the Buddhist era: it renders May 2026 as พฤษภาคม 2026 under a screen
 * saying 2569 everywhere else. `Intl` with the calendar spelled out does.
 *
 * Here rather than beside a screen because three screens draw a calendar and
 * this was already written out twice. The accessible names have the same
 * problem and are solved once in `lib/i18n/calendar-labels.ts`, which
 * `components/ui/calendar.tsx` applies to every caller; these cannot go there
 * too, because /admin/holidays needs its own pair forced into UTC.
 *
 * The dates handed in are the picker's own local-midnight days, so no timezone
 * is forced on them.
 */
export function calendarChrome(locale: Locale) {
  const tag = TAGS[locale]
  return {
    formatCaption: (d: Date) =>
      d.toLocaleDateString(tag, { month: "long", year: "numeric" }),
    formatMonthDropdown: (d: Date) =>
      d.toLocaleDateString(tag, { month: "short" }),
    formatYearDropdown: (d: Date) =>
      d.toLocaleDateString(tag, { year: "numeric" }),
    formatWeekdayName: (d: Date) =>
      d.toLocaleDateString(tag, { weekday: WEEKDAY_WIDTH[locale] }),
  }
}

/** A day, from its `YYYY-MM-DD` string, without ever becoming an instant. */
function dayAsDate(date: string): Date {
  const [y, m, d] = date.split("-").map(Number)
  return new Date(Date.UTC(y!, m! - 1, d!))
}

/**
 * Weekday, day and month. No year.
 *
 * For lists whose window never spans more than a month, where every row shares
 * a year and printing it would be noise on each of them.
 */
export function shortDay(date: string, locale: Locale): string {
  return dayAsDate(date).toLocaleDateString(TAGS[locale], {
    timeZone: "UTC",
    weekday: "short",
    day: "numeric",
    month: "short",
  })
}

/**
 * Day and month, and nothing else.
 *
 * For the two ends of a window written as one phrase, "24 Jul → 3 Aug", where
 * the weekday `shortDay` carries would double the length of a cell that has to
 * hold two dates and an arrow.
 */
export function dayAndMonth(date: string, locale: Locale): string {
  return dayAsDate(date).toLocaleDateString(TAGS[locale], {
    timeZone: "UTC",
    day: "numeric",
    month: "short",
  })
}

/**
 * The whole day, year included.
 *
 * For a page that can open a task from any year, where a date without one is a
 * date somebody has to guess at.
 */
export function fullDay(date: string, locale: Locale): string {
  return dayAsDate(date).toLocaleDateString(TAGS[locale], {
    timeZone: "UTC",
    weekday: "long",
    day: "numeric",
    month: "long",
    year: "numeric",
  })
}

/**
 * The month and the year it belongs to.
 *
 * What a calendar grid is called when it is announced as a whole: "April 2026",
 * "เมษายน พ.ศ. 2569". It takes a day, like everything else here, because the
 * caller holding a month is holding the first of it.
 */
export function monthAndYear(date: string, locale: Locale): string {
  return dayAsDate(date).toLocaleDateString(TAGS[locale], {
    timeZone: "UTC",
    month: "long",
    year: "numeric",
  })
}

/**
 * The month a day belongs to, named and nothing else.
 *
 * For a sentence about the period rather than the date: the month that produced
 * an instance, or the month a quarterly duty skips. It takes a day and not a
 * year and a number, because every caller already holds the day.
 */
export function monthName(date: string, locale: Locale): string {
  return dayAsDate(date).toLocaleDateString(TAGS[locale], {
    timeZone: "UTC",
    month: "long",
  })
}

/** A moment, to the minute, in Bangkok. */
export function moment(at: Date, locale: Locale): string {
  return at.toLocaleString(TAGS[locale], {
    timeZone: "Asia/Bangkok",
    day: "numeric",
    month: "short",
    hour: "2-digit",
    minute: "2-digit",
  })
}
