import {
  DAYS_PER_WEEK,
  calculateCalendarDimensions,
  getCellType,
  getDayNumber,
} from 'shared-utils/calendar/calendar-grid'
import {
  getDayViewData,
  getDayCellClasses,
  getDayButtonClasses,
  type IDayViewContext,
  type IDayCellClassContext,
} from './calendar-utils'
import type { ICalendarState } from './types'

export const CalendarGrid = ({ state }: { state: ICalendarState }) => {
  const {
    strings,
    year,
    month,
    activeSelected,
    focusedDate,
    inRange,
    rangeHovered,
    range,
    multiple,
    weeknumbers,
    selectableDatesRef,
    tabIndexSetRef,
    handleDateSelect,
    handleRangeHover,
    isExcluded,
    isDayDisabled,
    setFocusedDate,
    todayDate,
  } = state

  const dayViewCtx: IDayViewContext = {
    year,
    month,
    activeSelected,
    focusedDate,
    tabIndexSetRef,
    isDayDisabled,
  }

  const cellClassCtx: IDayCellClassContext = {
    range,
    activeSelected,
    rangeHovered,
    inRange,
    isExcluded,
  }

  const renderDayNames = () => {
    const headers: React.ReactNode[] = []

    if (weeknumbers) {
      headers.push(
        <th key="week-header">
          <div className="pkt-calendar__week-number">{strings.week}</div>
        </th>,
      )
    }

    for (let i = 0; i < strings.daysShort.length; i++) {
      headers.push(
        <th key={`day-${i}`}>
          <div className="pkt-calendar__day-name" aria-label={strings.days[i]}>
            {strings.daysShort[i]}
          </div>
        </th>,
      )
    }

    return <tr className="pkt-cal-week-row">{headers}</tr>
  }

  const renderDayView = (dayCounter: number, today: Date) => {
    const data = getDayViewData(dayCounter, today, dayViewCtx)
    const { currentDate, currentDateISO, isSelected, isDisabled: isDisabledVal, ariaLabel, tabindex } = data

    selectableDatesRef.current.push({ currentDateISO, isDisabled: isDisabledVal, tabindex })

    const cellClasses = getDayCellClasses(data, cellClassCtx)
    const buttonClasses = getDayButtonClasses(data, cellClassCtx)

    return (
      <td key={currentDateISO} className={cellClasses}>
        <button
          type="button"
          aria-pressed={isSelected ? 'true' : 'false'}
          disabled={isDisabledVal}
          className={`pkt-btn pkt-btn--tertiary pkt-btn--small pkt-btn--label-only ${buttonClasses}`}
          onMouseOver={() => range && !isExcluded(currentDate) && handleRangeHover(currentDate)}
          onFocus={() => {
            if (range && !isExcluded(currentDate)) {
              handleRangeHover(currentDate)
            }
            setFocusedDate(currentDateISO)
          }}
          aria-label={ariaLabel}
          tabIndex={parseInt(tabindex)}
          data-disabled={isDisabledVal ? 'disabled' : undefined}
          data-date={currentDateISO}
          onKeyDown={(e) => {
            if (e.key === 'Enter' || e.key === ' ') {
              e.preventDefault()
              handleDateSelect(currentDate)
            }
          }}
          onClick={(e) => {
            if (!isDisabledVal) {
              e.preventDefault()
              handleDateSelect(currentDate)
            }
          }}
        >
          <span className="pkt-btn__text pkt-txt-14-light">{dayCounter}</span>
        </button>
      </td>
    )
  }

  const renderEmptyDayCell = (day: number, key: string) => (
    <td key={key} className="pkt-cal-other">
      <div className="pkt-btn pkt-btn--tertiary pkt-btn--small pkt-btn--label-only" data-disabled="disabled">
        <span className="pkt-btn__text pkt-txt-14-light">{day}</span>
      </div>
    </td>
  )

  const renderCalendarBody = () => {
    const today = todayDate
    const dimensions = calculateCalendarDimensions(year, month)

    selectableDatesRef.current = []
    tabIndexSetRef.current = 0

    let dayCounter = 1
    let currentWeek = dimensions.initialWeek

    const rows: React.ReactNode[] = []

    for (let i = 0; i < dimensions.numRows; i++) {
      const cells: React.ReactNode[] = []

      if (weeknumbers) {
        cells.push(
          <td key={`week-${currentWeek}`} className="pkt-cal-week">
            {currentWeek}
          </td>,
        )
      }
      currentWeek++

      for (let j = 0; j < DAYS_PER_WEEK; j++) {
        const cellType = getCellType(i, j, dayCounter, dimensions)

        if (cellType === 'current-month') {
          cells.push(renderDayView(dayCounter, today))
          dayCounter++
        } else {
          const dayNumber = getDayNumber(cellType, j, dayCounter, dimensions)
          cells.push(renderEmptyDayCell(dayNumber, `${cellType}-${i}-${j}`))
          if (cellType === 'next-month') {
            dayCounter++
          }
        }
      }

      rows.push(
        <tr key={`row-${i}`} className="pkt-cal-week-row" role="row">
          {cells}
        </tr>,
      )
    }

    return rows
  }

  return (
    <table
      className="pkt-cal-days pkt-txt-12-medium pkt-calendar__body"
      role="grid"
      aria-multiselectable={range || multiple || undefined}
    >
      <thead>{renderDayNames()}</thead>
      <tbody>{renderCalendarBody()}</tbody>
    </table>
  )
}
