'use client';

import * as React from 'react';
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react';
import { useDayPicker, type MonthCaptionProps } from 'react-day-picker';

import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Calendar, CalendarDayButton } from '@/components/ui/calendar';

type CalendarView = 'days' | 'months' | 'years';

const MONTH_LABELS = [
  'Jan',
  'Feb',
  'Mar',
  'Apr',
  'May',
  'Jun',
  'Jul',
  'Aug',
  'Sep',
  'Oct',
  'Nov',
  'Dec',
] as const;

const PICKER_WIDTH = 'w-[280px]';
const PICKER_SHELL_CLASS =
  'group/calendar p-3 text-popover-foreground [--cell-size:--spacing(8)]';
const PICKER_HEADER_CLASS =
  'grid min-h-8 grid-cols-[2rem_1fr_2rem] items-center gap-1 border-b border-border pb-2';
const PICKER_NAV_BUTTON_CLASS = 'size-8 shrink-0 p-0';
const GRID_BUTTON_CLASS =
  'h-9 w-full rounded-lg font-medium text-foreground hover:bg-accent/60 hover:text-accent-foreground';
const GRID_BUTTON_ACTIVE_CLASS =
  'bg-muted text-foreground hover:bg-muted hover:text-foreground';
const GRID_BUTTON_DISABLED_CLASS =
  'text-muted-foreground opacity-50 hover:bg-transparent hover:text-muted-foreground';

function getMonthIndex(date: Date) {
  return date.getFullYear() * 12 + date.getMonth();
}

function isYearOutsideRange(year: number, startMonth?: Date, endMonth?: Date) {
  if (startMonth && year < startMonth.getFullYear()) return true;
  if (endMonth && year > endMonth.getFullYear()) return true;
  return false;
}

function isMonthOutsideRange(
  year: number,
  month: number,
  startMonth?: Date,
  endMonth?: Date,
) {
  const monthIndex = year * 12 + month;

  if (startMonth && monthIndex < getMonthIndex(startMonth)) return true;
  if (endMonth && monthIndex > getMonthIndex(endMonth)) return true;

  return false;
}

function clampMonthToRange(
  year: number,
  month: number,
  startMonth?: Date,
  endMonth?: Date,
) {
  let nextMonth = month;

  if (startMonth && year === startMonth.getFullYear()) {
    nextMonth = Math.max(nextMonth, startMonth.getMonth());
  }

  if (endMonth && year === endMonth.getFullYear()) {
    nextMonth = Math.min(nextMonth, endMonth.getMonth());
  }

  return Math.min(11, Math.max(0, nextMonth));
}

type CalendarGridPickerContextValue = {
  view: CalendarView;
  setView: (view: CalendarView) => void;
  pickerYear: number;
  setPickerYear: React.Dispatch<React.SetStateAction<number>>;
  decadeStart: number;
  setDecadeStart: React.Dispatch<React.SetStateAction<number>>;
};

const CalendarGridPickerContext =
  React.createContext<CalendarGridPickerContextValue | null>(null);

function useCalendarGridPicker() {
  const context = React.useContext(CalendarGridPickerContext);
  if (!context) {
    throw new Error(
      'useCalendarGridPicker must be used within CalendarGridPicker',
    );
  }
  return context;
}

function PickerNav({
  label,
  onPrevious,
  onNext,
  onLabelClick,
  disablePrevious = false,
  disableNext = false,
}: {
  label: string;
  onPrevious: () => void;
  onNext: () => void;
  onLabelClick?: () => void;
  disablePrevious?: boolean;
  disableNext?: boolean;
}) {
  return (
    <div className={PICKER_HEADER_CLASS}>
      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        className={cn(PICKER_NAV_BUTTON_CLASS, 'justify-self-start')}
        onClick={onPrevious}
        disabled={disablePrevious}
        aria-label="Previous"
      >
        <ChevronLeftIcon className="size-4" />
      </Button>
      {onLabelClick ? (
        <button
          type="button"
          className="rounded-lg px-2 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
          onClick={onLabelClick}
        >
          {label}
        </button>
      ) : (
        <span className="px-2 text-center text-sm font-semibold text-foreground">
          {label}
        </span>
      )}
      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        className={cn(PICKER_NAV_BUTTON_CLASS, 'justify-self-end')}
        onClick={onNext}
        disabled={disableNext}
        aria-label="Next"
      >
        <ChevronRightIcon className="size-4" />
      </Button>
    </div>
  );
}

function MonthGrid({
  year,
  activeMonth,
  onSelect,
  startMonth,
  endMonth,
}: {
  year: number;
  activeMonth: number;
  onSelect: (month: number) => void;
  startMonth?: Date;
  endMonth?: Date;
}) {
  return (
    <div className="grid grid-cols-3 gap-x-2 gap-y-1.5 pt-2">
      {MONTH_LABELS.map((label, month) => {
        const isDisabled = isMonthOutsideRange(
          year,
          month,
          startMonth,
          endMonth,
        );

        return (
          <Button
            key={label}
            type="button"
            variant="ghost"
            disabled={isDisabled}
            className={cn(
              GRID_BUTTON_CLASS,
              isDisabled && GRID_BUTTON_DISABLED_CLASS,
              activeMonth === month && !isDisabled && GRID_BUTTON_ACTIVE_CLASS,
            )}
            onClick={() => onSelect(month)}
          >
            {label}
          </Button>
        );
      })}
    </div>
  );
}

function YearGrid({
  decadeStart,
  activeYear,
  onSelect,
  startMonth,
  endMonth,
}: {
  decadeStart: number;
  activeYear: number;
  onSelect: (year: number) => void;
  startMonth?: Date;
  endMonth?: Date;
}) {
  const years = React.useMemo(
    () => Array.from({ length: 10 }, (_, index) => decadeStart + index),
    [decadeStart],
  );

  return (
    <div className="grid grid-cols-2 gap-x-3 gap-y-1.5 pt-2">
      {years.map((year) => {
        const isDisabled = isYearOutsideRange(year, startMonth, endMonth);

        return (
          <Button
            key={year}
            type="button"
            variant="ghost"
            disabled={isDisabled}
            className={cn(
              GRID_BUTTON_CLASS,
              isDisabled && GRID_BUTTON_DISABLED_CLASS,
              activeYear === year && !isDisabled && GRID_BUTTON_ACTIVE_CLASS,
            )}
            onClick={() => onSelect(year)}
          >
            {year}
          </Button>
        );
      })}
    </div>
  );
}

function CalendarMonthCaption({
  calendarMonth,
  className,
  children: _children,
  displayIndex: _displayIndex,
  ...props
}: MonthCaptionProps) {
  const { goToMonth, previousMonth, nextMonth } = useDayPicker();
  const { setView, setPickerYear, setDecadeStart } = useCalendarGridPicker();
  const date = calendarMonth.date;
  const monthName = date.toLocaleString('default', { month: 'long' });
  const year = date.getFullYear();

  return (
    <div className={cn(PICKER_HEADER_CLASS, className)} {...props}>
      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        className={cn(PICKER_NAV_BUTTON_CLASS, 'justify-self-start')}
        disabled={!previousMonth}
        aria-label="Previous month"
        onClick={() => previousMonth && goToMonth(previousMonth)}
      >
        <ChevronLeftIcon className="size-4" />
      </Button>

      <div className="flex items-center justify-center gap-1 text-sm font-semibold">
        <button
          type="button"
          className="rounded-lg px-1 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
          onClick={() => {
            setPickerYear(year);
            setView('months');
          }}
        >
          {monthName}
        </button>
        <button
          type="button"
          className="rounded-lg px-1 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
          onClick={() => {
            setDecadeStart(Math.floor(year / 10) * 10);
            setPickerYear(year);
            setView('years');
          }}
        >
          {year}
        </button>
      </div>

      <Button
        type="button"
        variant="ghost"
        size="icon-sm"
        className={cn(PICKER_NAV_BUTTON_CLASS, 'justify-self-end')}
        disabled={!nextMonth}
        aria-label="Next month"
        onClick={() => nextMonth && goToMonth(nextMonth)}
      >
        <ChevronRightIcon className="size-4" />
      </Button>
    </div>
  );
}

function CalendarGridPickerPanel({
  month,
  onMonthChange,
  startMonth,
  endMonth,
}: {
  month: Date;
  onMonthChange: (month: Date) => void;
  startMonth?: Date;
  endMonth?: Date;
}) {
  const {
    view,
    setView,
    pickerYear,
    setPickerYear,
    decadeStart,
    setDecadeStart,
  } = useCalendarGridPicker();

  const minYear = startMonth?.getFullYear();
  const maxYear = endMonth?.getFullYear();
  const activeMonth = clampMonthToRange(
    pickerYear,
    month.getMonth(),
    startMonth,
    endMonth,
  );

  if (view === 'months') {
    return (
      <div className="w-full">
        <PickerNav
          label={String(pickerYear)}
          onPrevious={() => setPickerYear((year) => year - 1)}
          onNext={() => setPickerYear((year) => year + 1)}
          onLabelClick={() => {
            setDecadeStart(Math.floor(pickerYear / 10) * 10);
            setView('years');
          }}
          disablePrevious={minYear !== undefined && pickerYear <= minYear}
          disableNext={maxYear !== undefined && pickerYear >= maxYear}
        />
        <MonthGrid
          year={pickerYear}
          activeMonth={activeMonth}
          startMonth={startMonth}
          endMonth={endMonth}
          onSelect={(nextMonth) => {
            onMonthChange(new Date(pickerYear, nextMonth, 1));
            setView('days');
          }}
        />
      </div>
    );
  }

  if (view === 'years') {
    return (
      <div className="w-full">
        <PickerNav
          label={`${decadeStart} - ${decadeStart + 9}`}
          onPrevious={() => setDecadeStart((start) => start - 10)}
          onNext={() => setDecadeStart((start) => start + 10)}
          disablePrevious={minYear !== undefined && decadeStart <= minYear}
          disableNext={maxYear !== undefined && decadeStart + 10 > maxYear}
        />
        <YearGrid
          decadeStart={decadeStart}
          activeYear={pickerYear}
          startMonth={startMonth}
          endMonth={endMonth}
          onSelect={(year) => {
            const nextMonth = clampMonthToRange(
              year,
              activeMonth,
              startMonth,
              endMonth,
            );

            setPickerYear(year);
            onMonthChange(new Date(year, nextMonth, 1));
            setView('months');
          }}
        />
      </div>
    );
  }

  return null;
}

export type CalendarGridPickerProps = Omit<
  Extract<React.ComponentProps<typeof Calendar>, { mode: 'single' }>,
  'captionLayout' | 'navLayout' | 'hideNavigation'
> & {
  onViewChange?: (view: CalendarView) => void;
};

function CalendarGridPicker({
  className,
  classNames,
  components,
  formatters,
  month,
  defaultMonth,
  onMonthChange,
  onViewChange,
  startMonth,
  endMonth,
  ...props
}: CalendarGridPickerProps) {
  const initialMonth = month ?? defaultMonth ?? new Date();
  const [view, setView] = React.useState<CalendarView>('days');
  const [pickerYear, setPickerYear] = React.useState(
    initialMonth.getFullYear(),
  );
  const [decadeStart, setDecadeStart] = React.useState(
    Math.floor(initialMonth.getFullYear() / 10) * 10,
  );
  const [internalMonth, setInternalMonth] = React.useState(initialMonth);

  const currentMonth = month ?? internalMonth;

  React.useEffect(() => {
    if (month) {
      setPickerYear(month.getFullYear());
      setDecadeStart(Math.floor(month.getFullYear() / 10) * 10);
    }
  }, [month]);

  const handleViewChange = React.useCallback(
    (nextView: CalendarView) => {
      setView(nextView);
      onViewChange?.(nextView);
    },
    [onViewChange],
  );

  const handleMonthChange = React.useCallback(
    (nextMonth: Date) => {
      if (!month) setInternalMonth(nextMonth);
      setPickerYear(nextMonth.getFullYear());
      setDecadeStart(Math.floor(nextMonth.getFullYear() / 10) * 10);
      onMonthChange?.(nextMonth);
    },
    [month, onMonthChange],
  );

  const contextValue = React.useMemo(
    () => ({
      view,
      setView: handleViewChange,
      pickerYear,
      setPickerYear,
      decadeStart,
      setDecadeStart,
    }),
    [view, handleViewChange, pickerYear, decadeStart],
  );

  if (view !== 'days') {
    return (
      <CalendarGridPickerContext.Provider value={contextValue}>
        <div className={cn(PICKER_SHELL_CLASS, PICKER_WIDTH, className)}>
          <CalendarGridPickerPanel
            month={currentMonth}
            onMonthChange={handleMonthChange}
            startMonth={startMonth}
            endMonth={endMonth}
          />
        </div>
      </CalendarGridPickerContext.Provider>
    );
  }

  return (
    <CalendarGridPickerContext.Provider value={contextValue}>
      <Calendar
        hideNavigation
        className={cn(PICKER_SHELL_CLASS, PICKER_WIDTH, className)}
        month={currentMonth}
        onMonthChange={handleMonthChange}
        startMonth={startMonth}
        endMonth={endMonth}
        formatters={{
          formatWeekdayName: (date) =>
            date.toLocaleString('en-US', { weekday: 'short' }).slice(0, 2),
          ...formatters,
        }}
        classNames={{
          month: 'flex w-full flex-col gap-2',
          month_caption: 'p-0',
          weekdays: 'flex w-full',
          weekday:
            'flex-1 text-center text-[0.8rem] font-medium text-muted-foreground select-none',
          week: 'mt-1 flex w-full',
          ...classNames,
        }}
        components={{
          MonthCaption: CalendarMonthCaption,
          DayButton: (dayButtonProps) => (
            <CalendarDayButton
              {...dayButtonProps}
              className={cn(
                dayButtonProps.className,
                'data-[selected-single=true]:rounded-full data-[selected-single=true]:bg-muted data-[selected-single=true]:text-foreground',
              )}
            />
          ),
          ...components,
        }}
        {...(props as Extract<
          React.ComponentProps<typeof Calendar>,
          { mode: 'single' }
        >)}
      />
    </CalendarGridPickerContext.Provider>
  );
}

export { CalendarGridPicker, type CalendarView };
