/**
 * Форматирование и лёгкий клиентский предпросмотр правила повторения
 * конференции (`ConferenceRecurrence`). Источник истины по развёртке —
 * backend (`backend/services/recurrence.py::expand_occurrences`); функции
 * ниже — только для UI-подсказок в форме создания/редактирования
 * («ближайшие вхождения») и подписей в карточках календаря/«Моих
 * конференций» — точность до секунды/переход через полночь не гарантируется.
 */
import type { ConferenceRecurrence, RecurrenceType } from '@/api/conferences'

/** Короткие подписи дней недели для чипов (0 — понедельник .. 6 — воскресенье). */
export const WEEKDAY_SHORT = ['ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ', 'ВС'] as const

const WEEKDAY_DATIVE_PLURAL = [
  'понедельникам',
  'вторникам',
  'средам',
  'четвергам',
  'пятницам',
  'субботам',
  'воскресеньям',
] as const

function pad2(n: number): string {
  return String(n).padStart(2, '0')
}

/** Прибавить минуты к времени вида "HH:MM" (без учёта перехода через полночь на новый день). */
export function addMinutesToTime(hhmm: string, minutes: number): string {
  const [h, m] = hhmm.split(':').map(Number)
  const total = ((h * 60 + m + minutes) % (24 * 60) + 24 * 60) % (24 * 60)
  return `${pad2(Math.floor(total / 60))}:${pad2(total % 60)}`
}

/** Соединить список строк по-русски («а» / «а и б» / «а, б и в»). */
function joinRu(items: string[]): string {
  if (items.length === 0) return ''
  if (items.length === 1) return items[0]
  return `${items.slice(0, -1).join(', ')} и ${items[items.length - 1]}`
}

/** Человекочитаемое описание правила повторения + диапазон времени вхождения. */
export function formatRecurrenceSummary(recurrence: ConferenceRecurrence): string {
  const range = `${recurrence.time_local}–${addMinutesToTime(recurrence.time_local, recurrence.duration_minutes)}`
  switch (recurrence.type) {
    case 'weekly': {
      const days = [...(recurrence.weekdays ?? [])].sort((a, b) => a - b).map((w) => WEEKDAY_DATIVE_PLURAL[w])
      return `По ${joinRu(days)}, ${range}`
    }
    case 'biweekly': {
      const days = [...(recurrence.weekdays ?? [])].sort((a, b) => a - b).map((w) => WEEKDAY_DATIVE_PLURAL[w])
      return `Раз в 2 недели, по ${joinRu(days)}, ${range}`
    }
    case 'monthly':
      return `Раз в месяц, ${recurrence.day_of_month}-го числа, ${range}`
    case 'every_n_days':
      return `Каждые ${recurrence.interval_days} дней, ${range}`
    default:
      return range
  }
}

/** Разобрать дату вида "YYYY-MM-DD" в локальную полночь (без смещения часового пояса). */
function parseDateOnly(value: string): Date {
  const [y, m, d] = value.split('-').map(Number)
  return new Date(y, m - 1, d)
}

function startOfWeek(d: Date): Date {
  const jsWeekday = (d.getDay() + 6) % 7 // 0 — понедельник
  const out = new Date(d)
  out.setDate(out.getDate() - jsWeekday)
  out.setHours(0, 0, 0, 0)
  return out
}

function matchesCalendarDate(
  type: RecurrenceType,
  weekdays: number[],
  dayOfMonth: number | undefined,
  intervalDays: number | undefined,
  anchor: Date,
  candidate: Date,
): boolean {
  const ourWeekday = (candidate.getDay() + 6) % 7
  if (type === 'weekly') return weekdays.includes(ourWeekday)
  if (type === 'biweekly') {
    if (!weekdays.includes(ourWeekday)) return false
    const weeksDiff = Math.round((startOfWeek(candidate).getTime() - startOfWeek(anchor).getTime()) / (7 * 86_400_000))
    return ((weeksDiff % 2) + 2) % 2 === 0
  }
  if (type === 'monthly') {
    const lastDay = new Date(candidate.getFullYear(), candidate.getMonth() + 1, 0).getDate()
    const clamped = Math.min(dayOfMonth ?? 1, lastDay)
    return candidate.getDate() === clamped
  }
  if (type === 'every_n_days') {
    if (candidate < anchor) return false
    const daysDiff = Math.round((candidate.getTime() - anchor.getTime()) / 86_400_000)
    return daysDiff % Math.max(1, intervalDays ?? 1) === 0
  }
  return false
}

/**
 * Приблизительный клиентский предпросмотр ближайших `count` дат вхождения —
 * только для подсказки в форме (см. заголовок файла).
 */
export function previewNextDates(
  recurrence: Pick<ConferenceRecurrence, 'type' | 'weekdays' | 'day_of_month' | 'interval_days' | 'anchor_date'>,
  count = 2,
): Date[] {
  const anchor = parseDateOnly(recurrence.anchor_date)
  const today = new Date()
  today.setHours(0, 0, 0, 0)
  const start = anchor > today ? anchor : today
  const result: Date[] = []
  const cursor = new Date(start)
  for (let i = 0; i < 3660 && result.length < count; i++) {
    if (
      matchesCalendarDate(
        recurrence.type,
        recurrence.weekdays ?? [],
        recurrence.day_of_month,
        recurrence.interval_days,
        anchor,
        cursor,
      )
    ) {
      result.push(new Date(cursor))
    }
    cursor.setDate(cursor.getDate() + 1)
  }
  return result
}

const DATE_HINT_FORMAT: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'long' }

/** Отформатировать список дат для подсказки вида «ближайшие: вторник 21 июля, четверг 23 июля». */
export function formatPreviewHint(dates: Date[]): string {
  if (dates.length === 0) return ''
  const labels = dates.map((d) => d.toLocaleDateString('ru-RU', DATE_HINT_FORMAT))
  return `Ближайшие: ${joinRu(labels)}`
}
