"use client";

import * as React from "react";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { cn } from "../../lib/utils";
import { buttonVariants } from "./button";
import { 
  format, 
  startOfMonth, 
  endOfMonth, 
  eachDayOfInterval, 
  getDay, 
  isSameMonth, 
  isSameDay,
  isToday,
  addMonths,
  subMonths,
  startOfWeek,
  endOfWeek
} from "date-fns";

export interface CalendarProps {
  mode?: "single" | "range" | "multiple";
  selected?: Date | Date[] | { from?: Date; to?: Date } | undefined;
  onSelect?: (date: Date | Date[] | { from?: Date; to?: Date } | undefined) => void;
  disabled?: (date: Date) => boolean;
  showOutsideDays?: boolean;
  className?: string;
  classNames?: Record<string, string>;
  numberOfMonths?: number;
  defaultMonth?: Date;
  initialFocus?: boolean;
}

function Calendar({
  mode = "single",
  selected,
  onSelect,
  disabled,
  showOutsideDays = false,
  className,
  classNames,
  numberOfMonths = 1,
  defaultMonth,
  ...props
}: CalendarProps) {
  const getInitialMonth = () => {
    if (defaultMonth) return defaultMonth;
    if (mode === "range" && selected) {
      const range = selected as { from?: Date; to?: Date };
      if (range.from && range.from instanceof Date) return range.from;
    }
    if (selected && selected instanceof Date) return selected;
    return new Date();
  };

  const [currentMonth, setCurrentMonth] = React.useState(getInitialMonth());

  const weekDays = [
    { short: "S", full: "Sunday" },
    { short: "M", full: "Monday" },
    { short: "T", full: "Tuesday" },
    { short: "W", full: "Wednesday" },
    { short: "T", full: "Thursday" },
    { short: "F", full: "Friday" },
    { short: "S", full: "Saturday" }
  ];

  const handlePreviousMonth = () => {
    setCurrentMonth(subMonths(currentMonth, 1));
  };

  const handleNextMonth = () => {
    setCurrentMonth(addMonths(currentMonth, 1));
  };

  const handleDateClick = (date: Date) => {
    if (disabled?.(date)) return;

    if (mode === "single") {
      onSelect?.(date);
    } else if (mode === "range") {
      const currentSelection = selected as { from?: Date; to?: Date } | undefined;
      if (!currentSelection?.from || (currentSelection.from && currentSelection.to)) {
        onSelect?.({ from: date, to: undefined });
      } else {
        if (date < currentSelection.from) {
          onSelect?.({ from: date, to: currentSelection.from });
        } else {
          onSelect?.({ from: currentSelection.from, to: date });
        }
      }
    }
  };

  const isDateSelected = (date: Date) => {
    if (!selected) return false;
    
    if (mode === "single") {
      return isSameDay(date, selected as Date);
    } else if (mode === "range") {
      const range = selected as { from?: Date; to?: Date };
      if (range.from && range.to) {
        return date >= range.from && date <= range.to;
      }
      return range.from ? isSameDay(date, range.from) : false;
    }
    return false;
  };

  const isRangeStart = (date: Date) => {
    if (mode !== "range" || !selected) return false;
    const range = selected as { from?: Date; to?: Date };
    return range.from ? isSameDay(date, range.from) : false;
  };

  const isRangeEnd = (date: Date) => {
    if (mode !== "range" || !selected) return false;
    const range = selected as { from?: Date; to?: Date };
    return range.to ? isSameDay(date, range.to) : false;
  };

  const isRangeMiddle = (date: Date) => {
    if (mode !== "range" || !selected) return false;
    const range = selected as { from?: Date; to?: Date };
    if (!range.from || !range.to) return false;
    return date > range.from && date < range.to;
  };

  const getDaysInMonth = () => {
    const start = startOfMonth(currentMonth);
    const end = endOfMonth(currentMonth);
    const days = eachDayOfInterval({ start, end });
    
    // Get days from previous month to fill the first week
    const firstDayOfWeek = getDay(start);
    const previousMonthDays = [];
    if (firstDayOfWeek > 0 && showOutsideDays) {
      const previousMonthStart = startOfWeek(start);
      const previousMonthEnd = new Date(start);
      previousMonthEnd.setDate(previousMonthEnd.getDate() - 1);
      previousMonthDays.push(...eachDayOfInterval({ 
        start: previousMonthStart, 
        end: previousMonthEnd 
      }));
    }
    
    // Get days from next month to fill the last week
    const lastDayOfWeek = getDay(end);
    const nextMonthDays = [];
    if (lastDayOfWeek < 6 && showOutsideDays) {
      const nextMonthStart = new Date(end);
      nextMonthStart.setDate(nextMonthStart.getDate() + 1);
      const nextMonthEnd = endOfWeek(end);
      nextMonthDays.push(...eachDayOfInterval({ 
        start: nextMonthStart, 
        end: nextMonthEnd 
      }));
    }
    
    // Add empty cells for the first week if not showing outside days
    const emptyCells = [];
    if (!showOutsideDays && firstDayOfWeek > 0) {
      for (let i = 0; i < firstDayOfWeek; i++) {
        emptyCells.push(null);
      }
    }
    
    return [...previousMonthDays, ...emptyCells, ...days, ...nextMonthDays];
  };

  const renderCalendar = (monthOffset: number = 0) => {
    const displayMonth = addMonths(currentMonth, monthOffset);
    const start = startOfMonth(displayMonth);
    const end = endOfMonth(displayMonth);
    const days = eachDayOfInterval({ start, end });
    
    // Get days from previous month to fill the first week
    const firstDayOfWeek = getDay(start);
    const previousMonthDays = [];
    if (firstDayOfWeek > 0 && showOutsideDays) {
      const previousMonthStart = startOfWeek(start);
      const previousMonthEnd = new Date(start);
      previousMonthEnd.setDate(previousMonthEnd.getDate() - 1);
      previousMonthDays.push(...eachDayOfInterval({ 
        start: previousMonthStart, 
        end: previousMonthEnd 
      }));
    }
    
    // Get days from next month to fill the last week
    const lastDayOfWeek = getDay(end);
    const nextMonthDays = [];
    if (lastDayOfWeek < 6 && showOutsideDays) {
      const nextMonthStart = new Date(end);
      nextMonthStart.setDate(nextMonthStart.getDate() + 1);
      const nextMonthEnd = endOfWeek(end);
      nextMonthDays.push(...eachDayOfInterval({ 
        start: nextMonthStart, 
        end: nextMonthEnd 
      }));
    }
    
    // Add empty cells for the first week if not showing outside days
    const emptyCells = [];
    if (!showOutsideDays && firstDayOfWeek > 0) {
      for (let i = 0; i < firstDayOfWeek; i++) {
        emptyCells.push(null);
      }
    }
    
    const allDays = [...previousMonthDays, ...emptyCells, ...days, ...nextMonthDays];

    return (
      <div className="moonui-calendar-month flex-1 min-w-0">
        {/* Month header for multi-month display */}
        {numberOfMonths > 1 && (
          <div className="flex items-center justify-center pb-2">
            <h3 className="text-xs sm:text-sm font-medium">
              {displayMonth instanceof Date && !isNaN(displayMonth.getTime()) 
                ? format(displayMonth, "MMMM yyyy")
                : ""}
            </h3>
          </div>
        )}

        {/* Week days header for each month */}
        <div className="grid grid-cols-7 gap-0 mb-1">
          {weekDays.map((day, index) => (
            <div
              key={`weekday-${monthOffset}-${index}`}
              className="text-muted-foreground text-[0.65rem] sm:text-[0.75rem] font-normal h-6 sm:h-7 w-full flex items-center justify-center"
              title={day.full}
            >
              {day.short}
            </div>
          ))}
        </div>

        {/* Days grid */}
        <div className="grid grid-cols-7 gap-0">
          {allDays.map((date, index) => {
            if (!date) {
              return <div key={`empty-${monthOffset}-${index}`} className="h-8 sm:h-9 w-full" />;
            }

            const isOutsideMonth = !isSameMonth(date, displayMonth);
            const isDisabled = disabled?.(date) || false;
            const isSelected = isDateSelected(date);
            const isTodayDate = isToday(date);
            const rangeStart = isRangeStart(date);
            const rangeEnd = isRangeEnd(date);
            const rangeMiddle = isRangeMiddle(date);

            return (
              <button
                key={date.toISOString()}
                onClick={() => handleDateClick(date)}
                disabled={isDisabled}
                className={cn(
                  "h-8 sm:h-9 w-full p-0 font-normal",
                  "inline-flex items-center justify-center rounded-md",
                  "hover:bg-muted transition-colors text-xs sm:text-sm",
                  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
                  isOutsideMonth && "text-muted-foreground/50",
                  isDisabled && "text-muted-foreground/30 cursor-not-allowed hover:bg-transparent",
                  isSelected && !rangeMiddle && "bg-primary text-primary-foreground font-medium hover:bg-primary hover:text-primary-foreground",
                  isTodayDate && !isSelected && "bg-accent text-accent-foreground",
                  rangeMiddle && "bg-accent rounded-none",
                  rangeStart && "rounded-r-none",
                  rangeEnd && "rounded-l-none"
                )}
                type="button"
              >
                {date instanceof Date && !isNaN(date.getTime()) 
                  ? format(date, "d")
                  : ""}
              </button>
            );
          })}
        </div>
      </div>
    );
  };

  return (
    <div className={cn("moonui-calendar p-2 sm:p-3", className)}>
      {/* Header - only show for single month */}
      {numberOfMonths === 1 && (
        <div className="flex items-center justify-between px-1 pb-3">
        <button
          onClick={handlePreviousMonth}
          className={cn(
            buttonVariants({ variant: "outline" }),
            "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
          )}
          type="button"
        >
          <ChevronLeft className="h-4 w-4" />
        </button>
        
        <h2 className="text-sm font-medium">
          {currentMonth instanceof Date && !isNaN(currentMonth.getTime()) 
            ? format(currentMonth, "MMMM yyyy")
            : ""
          }
        </h2>
        
        <button
          onClick={handleNextMonth}
          className={cn(
            buttonVariants({ variant: "outline" }),
            "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
          )}
          type="button"
        >
          <ChevronRight className="h-4 w-4" />
        </button>
        </div>
      )}

      {/* Navigation for multiple months */}
      {numberOfMonths > 1 && (
        <div className="flex items-center justify-between px-1 pb-3">
          <button
            onClick={handlePreviousMonth}
            className={cn(
              buttonVariants({ variant: "outline" }),
              "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
            )}
            type="button"
          >
            <ChevronLeft className="h-4 w-4" />
          </button>
          
          <h2 className="text-xs sm:text-sm font-medium text-center">
            {currentMonth instanceof Date && !isNaN(currentMonth.getTime()) 
              ? `${format(currentMonth, "MMM yyyy")} - ${format(addMonths(currentMonth, numberOfMonths - 1), "MMM yyyy")}`
              : ""
            }
          </h2>
          
          <button
            onClick={handleNextMonth}
            className={cn(
              buttonVariants({ variant: "outline" }),
              "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
            )}
            type="button"
          >
            <ChevronRight className="h-4 w-4" />
          </button>
        </div>
      )}

      {/* Week days header for single month */}
      {numberOfMonths === 1 && (
        <div className="grid grid-cols-7 gap-0 mb-1">
          {weekDays.map((day, index) => (
            <div
              key={`weekday-${index}`}
              className="text-muted-foreground text-[0.65rem] sm:text-[0.75rem] font-normal h-6 sm:h-7 w-full flex items-center justify-center"
              title={day.full}
            >
              {day.short}
            </div>
          ))}
        </div>
      )}

      {/* Calendar(s) */}
      <div className={cn(
        numberOfMonths > 1 && "flex flex-col sm:flex-row gap-2 sm:gap-4"
      )}>
        {Array.from({ length: numberOfMonths }).map((_, i) => (
          <div key={i} className="flex-1">
            {renderCalendar(i)}
          </div>
        ))}
      </div>
    </div>
  );
}

Calendar.displayName = "Calendar";

export { Calendar };