import { cn } from "@/utils/class-merge";
import { BackwardIcon } from "@heroicons/react/24/solid";
import { FunctionComponent, h } from "preact";
import { useState } from "preact/hooks";

interface BackButtonProps {
  selectedDate: Date;
  onClick: () => void;
}

const BackButton: FunctionComponent<BackButtonProps> = ({ selectedDate, onClick }: BackButtonProps) => {
  const currentDate = new Date();

  return currentDate.getMonth() !== selectedDate.getMonth() ? (
    <div
      class="fixed bottom-4 left-8 hidden aspect-square h-24 cursor-pointer flex-col items-center justify-center rounded-full bg-[rgb(251,194,199,0.53)] shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] hover:bg-[rgb(251,194,199,0.7)] md:flex"
      onClick={onClick}
    >
      <BackwardIcon class="size-8 text-sm text-white" />
    </div>
  ) : null;
};

type MarkedDateType = "event" | "holiday" | "important" | string;
type MarkedDates = Record<string, MarkedDateType>;

interface BigCalendarProps {
  onDateSelect?: (date: Date) => void;
  markedDates?: MarkedDates;
  className?: string;
}

const BigCalendar: FunctionComponent<BigCalendarProps> = ({
  onDateSelect = () => {},
  markedDates = {},
  className = "",
}: BigCalendarProps) => {
  const [currentDate, setCurrentDate] = useState<Date>(new Date());
  const [selectedDate, setSelectedDate] = useState<Date | null>(null);

  const monthNames: string[] = [
    "Январь",
    "Февраль",
    "Март",
    "Апрель",
    "Май",
    "Июнь",
    "Июль",
    "Август",
    "Сентябрь",
    "Октябрь",
    "Ноябрь",
    "Декабрь",
  ];

  const dayNames: string[] = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"];

  const getDaysInMonth = (year: number, month: number): number => {
    return new Date(year, month + 1, 0).getDate();
  };

  const getFirstDayOfMonth = (year: number, month: number): number => {
    const day = new Date(year, month, 1).getDay();
    return day === 0 ? 6 : day;
  };

  const handlePrevMonth = (): void => {
    setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
  };

  const handleNextMonth = (): void => {
    setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
  };

  const handleDateClick = (date: Date, isCurrentMonth: boolean): void => {
    if (!isCurrentMonth) {
      setCurrentDate(new Date(date.getFullYear(), date.getMonth(), 1));
    }
    setSelectedDate(date);
    onDateSelect(date);
  };

  const isDateMarked = (date: Date): MarkedDateType | undefined => {
    const dateStr = date.toISOString().split("T")[0];
    return markedDates[dateStr];
  };

  const getLastDayOfMonth = (year: number, month: number): number => {
    const day = new Date(year, month + 1, 0).getDay();
    return day === 0 ? 6 : day;
  };

  const renderDays = (): h.JSX.Element[] => {
    const year: number = currentDate.getFullYear();
    const month: number = currentDate.getMonth();

    const daysInMonth: number = getDaysInMonth(year, month);
    const firstDayOfMonth: number = getFirstDayOfMonth(year, month);
    const lastDayOfMonth: number = getLastDayOfMonth(year, month);

    const days: h.JSX.Element[] = [];

    // Дни предыдущего месяца
    const prevMonthDays = getDaysInMonth(year, month - 1);
    const daysFromPrevMonth = firstDayOfMonth === 0 ? 6 : firstDayOfMonth;
    for (let i = daysFromPrevMonth - 1; i >= 0; i--) {
      const day = prevMonthDays - i;
      const date = new Date(year, month - 1, day);
      const dateStr = date.toISOString().split("T")[0];
      const isSelected = selectedDate?.toISOString().split("T")[0] === dateStr;
      const markType = isDateMarked(date);

      days.push(
        <div
          key={`prev-${day}`}
          className={cn(
            "relative flex h-24 cursor-pointer flex-col border border-gray-200 p-2 opacity-50 hover:opacity-70",
            { "bg-gray-200": isSelected }
          )}
          onClick={() => handleDateClick(date, false)}
        >
          <div className="flex h-8 w-8 items-center justify-center self-end rounded-full text-gray-600">{day}</div>
          {markType && (
            <div
              className={cn(
                "mb-1 truncate rounded p-1 text-xs",
                { "bg-green-100 text-green-800": markType === "event" },
                { "bg-red-100 text-red-800": markType === "holiday" },
                { "bg-yellow-100 text-yellow-800": markType === "important" }
              )}
            >
              {markType}
            </div>
          )}
        </div>
      );
    }

    // Дни текущего месяца
    for (let day = 1; day <= daysInMonth; day++) {
      const date = new Date(year, month, day);
      const dateStr = date.toISOString().split("T")[0];
      const isSelected = selectedDate?.toISOString().split("T")[0] === dateStr;
      const markType = isDateMarked(date);
      const isToday = new Date().toISOString().split("T")[0] === dateStr;

      days.push(
        <div
          key={`current-${day}`}
          className={cn(
            "relative flex h-24 flex-col border border-gray-200 p-2",
            { "border-blue-400 bg-blue-100": isSelected },
            { "border-yellow-400": isToday },
            "cursor-pointer hover:bg-gray-50"
          )}
          onClick={() => (selectedDate ? setSelectedDate(null) : handleDateClick(date, true))}
        >
          <div
            className={cn(
              "flex h-8 w-8 items-center justify-center self-end rounded-full",
              { "bg-blue-600 text-white": isSelected },
              { "bg-yellow-100 text-yellow-800": isToday && !isSelected }
            )}
          >
            {day}
          </div>

          {markType && (
            <div
              className={cn(
                "mb-1 truncate rounded p-1 text-xs",
                { "bg-green-100 text-green-800": markType === "event" },
                { "bg-red-100 text-red-800": markType === "holiday" },
                { "bg-yellow-100 text-yellow-800": markType === "important" }
              )}
            >
              {markType}
            </div>
          )}
        </div>
      );
    }

    // Дни следующего месяца
    const daysToAdd = 6 - (lastDayOfMonth === 6 ? 3 : lastDayOfMonth);
    for (let day = 1; day <= daysToAdd; day++) {
      const date = new Date(year, month + 1, day);
      const dateStr = date.toISOString().split("T")[0];
      const isSelected = selectedDate?.toISOString().split("T")[0] === dateStr;
      const markType = isDateMarked(date);

      days.push(
        <div
          key={`next-${day}`}
          className={cn(
            "relative flex h-24 cursor-pointer flex-col border border-gray-200 p-2 opacity-50 hover:opacity-70",
            { "bg-gray-200": isSelected }
          )}
          onClick={() => handleDateClick(date, false)}
        >
          <div className="flex h-8 w-8 items-center justify-center self-end rounded-full text-gray-600">{day}</div>
          {markType && (
            <div
              className={cn(
                "mb-1 truncate rounded p-1 text-xs",
                { "bg-green-100 text-green-800": markType === "event" },
                { "bg-red-100 text-red-800": markType === "holiday" },
                { "bg-yellow-100 text-yellow-800": markType === "important" }
              )}
            >
              {markType}
            </div>
          )}
        </div>
      );
    }

    return days;
  };

  return (
    <div className={`flex w-full flex-col ${className}`}>
      <div className="mb-4 flex items-center justify-between px-2">
        <button
          onClick={handlePrevMonth}
          className="rounded-lg p-2 text-gray-700 hover:bg-gray-100"
          aria-label="Previous month"
        >
          <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
            <path
              fillRule="evenodd"
              d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
              clipRule="evenodd"
            />
          </svg>
        </button>
        <h2 className="text-xl font-bold text-gray-800">
          {monthNames[currentDate.getMonth()]} {currentDate.getFullYear()}
        </h2>
        <button
          onClick={handleNextMonth}
          className="rounded-lg p-2 text-gray-700 hover:bg-gray-100"
          aria-label="Next month"
        >
          <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
            <path
              fillRule="evenodd"
              d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
              clipRule="evenodd"
            />
          </svg>
        </button>
      </div>
      <div className="mb-2 grid grid-cols-7 gap-1 text-center text-sm font-medium text-gray-500">
        {dayNames.map((day) => (
          <div key={day} className="flex h-10 items-center justify-center">
            {day}
          </div>
        ))}
      </div>
      <div className="grid flex-1 grid-cols-7 gap-1">{renderDays()}</div>
      <BackButton selectedDate={currentDate} onClick={() => setCurrentDate(new Date())} />
    </div>
  );
};

export default BigCalendar;
