import { TimeSlot, Participant } from '@/types/calendar';
import { cn } from '@/lib/utils';
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from '@/components/ui/popover';
import { Button } from '@/components/ui/button';
import { useState, useRef, useEffect } from 'react';
import { Check, X, Loader2, ChevronLeft, ChevronRight, ChevronsRight } from 'lucide-react';

const DEFAULT_TIMEZONE = 'America/Toronto';

const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
const SLOT_INTERVAL_MINUTES = 15;
const SLOTS_PER_HOUR = 60 / SLOT_INTERVAL_MINUTES;

// Generate time slots: [{hour: 0, minute: 0}, {hour: 0, minute: 30}, {hour: 1, minute: 0}, ...]
const timeSlots = Array.from({ length: 24 * SLOTS_PER_HOUR }, (_, i) => ({
  hour: Math.floor(i / SLOTS_PER_HOUR),
  minute: (i % SLOTS_PER_HOUR) * SLOT_INTERVAL_MINUTES,
}));

// Get the dates for Mon-Fri of a week in a specific timezone, offset by N weeks
const getWeekDates = (timezone: string, weekOffset: number = 0): string[] => {
  // Get "now" in the target timezone
  const now = new Date();
  const formatter = new Intl.DateTimeFormat('en-CA', {
    timeZone: timezone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  });

  // Parse today's date in the target timezone
  const todayStr = formatter.format(now);
  const [year, month, day] = todayStr.split('-').map(Number);

  // Calculate Monday of this week
  const todayDate = new Date(year, month - 1, day);
  const dayOfWeek = todayDate.getDay();
  const daysToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;

  const mondayDate = new Date(year, month - 1, day + daysToMonday + weekOffset * 7);

  return dayNames.map((_, i) => {
    const d = new Date(mondayDate);
    d.setDate(mondayDate.getDate() + i);
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, '0');
    const dd = String(d.getDate()).padStart(2, '0');
    return `${y}-${m}-${dd}`;
  });
};

// Convert a date string, hour, and minute in a timezone to a UTC Date
const toUTCDate = (dateStr: string, hour: number, minute: number, timezone: string): Date => {
  // Create a date string that represents the given time in the given timezone
  // Then parse it to get the UTC equivalent
  const localDateStr = `${dateStr}T${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:00`;

  // Use a trick: format in UTC then in target TZ to find the offset
  const testDate = new Date(localDateStr + 'Z'); // Treat as UTC first

  // Get what hour this would be in the target timezone
  const tzHour = parseInt(
    new Intl.DateTimeFormat('en-US', {
      timeZone: timezone,
      hour: 'numeric',
      hour12: false,
    }).format(testDate)
  );

  // Calculate offset in hours
  const offset = tzHour - testDate.getUTCHours();

  // Adjust: if we want `hour` in timezone, subtract the offset to get UTC
  const utcDate = new Date(localDateStr + 'Z');
  utcDate.setUTCHours(utcDate.getUTCHours() - offset);

  return utcDate;
};

const MIN_WEEK_OFFSET = 0;
const DEFAULT_MAX_WEEK_OFFSET = 1;
const EXPANDED_MAX_WEEK_OFFSET = 4;

// Format timezone for display (e.g., "America/Toronto" -> "Toronto (EST)")
const formatTimezoneDisplay = (timezone: string): string => {
  try {
    const parts = timezone.split('/');
    const city = parts[parts.length - 1].replace(/_/g, ' ');
    const now = new Date();
    const formatter = new Intl.DateTimeFormat('en-US', {
      timeZone: timezone,
      timeZoneName: 'short',
    });
    const formattedParts = formatter.formatToParts(now);
    const tzAbbrev = formattedParts.find((p) => p.type === 'timeZoneName')?.value || '';
    return `${city} (${tzAbbrev})`;
  } catch {
    return timezone;
  }
};

// Get timezone abbreviation (e.g., "America/Toronto" -> "EST")
const getTimezoneAbbrev = (timezone: string): string => {
  try {
    const now = new Date();
    const formatter = new Intl.DateTimeFormat('en-US', {
      timeZone: timezone,
      timeZoneName: 'short',
    });
    const parts = formatter.formatToParts(now);
    return parts.find((p) => p.type === 'timeZoneName')?.value || '';
  } catch {
    return '';
  }
};

// Convert a time from one timezone to another
const convertTimeBetweenTimezones = (
  hour: number,
  minute: number,
  dateStr: string,
  fromTimezone: string,
  toTimezone: string
): { hour: number; minute: number } => {
  try {
    // Create a UTC date for the given time in the source timezone
    const utcDate = toUTCDate(dateStr, hour, minute, fromTimezone);

    // Format the time in the target timezone
    const formatter = new Intl.DateTimeFormat('en-US', {
      timeZone: toTimezone,
      hour: 'numeric',
      minute: 'numeric',
      hour12: false,
    });
    const parts = formatter.formatToParts(utcDate);
    const targetHour = parseInt(parts.find(p => p.type === 'hour')?.value || '0');
    const targetMinute = parseInt(parts.find(p => p.type === 'minute')?.value || '0');

    return { hour: targetHour, minute: targetMinute };
  } catch {
    return { hour, minute };
  }
};

interface AvailabilityHeatmapProps {
  slots: TimeSlot[];
  selectedParticipants: Participant[];
  onSlotSelect: (slot: TimeSlot) => void;
  showPartialAvailability?: boolean;
  isLoading?: boolean;
  weekOffset?: number;
  onWeekOffsetChange?: (offset: number) => void;
  displayTimezone?: string;
  showSecondaryTimezone?: boolean;
  secondaryTimezone?: string;
}

export const AvailabilityHeatmap = ({
  slots,
  selectedParticipants,
  onSlotSelect,
  showPartialAvailability = false,
  isLoading = false,
  weekOffset = 0,
  onWeekOffsetChange,
  displayTimezone = DEFAULT_TIMEZONE,
  showSecondaryTimezone = false,
  secondaryTimezone = DEFAULT_TIMEZONE,
}: AvailabilityHeatmapProps) => {
  const [expanded, setExpanded] = useState(false);
  const maxWeekOffset = expanded ? EXPANDED_MAX_WEEK_OFFSET : DEFAULT_MAX_WEEK_OFFSET;
  const weekDates = getWeekDates(displayTimezone, weekOffset);

  // Get current time info in display timezone
  const getCurrentTimeInfo = () => {
    const now = new Date();
    const formatter = new Intl.DateTimeFormat('en-CA', {
      timeZone: displayTimezone,
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
    });
    const hourFormatter = new Intl.DateTimeFormat('en-US', {
      timeZone: displayTimezone,
      hour: 'numeric',
      hour12: false,
    });
    const todayStr = formatter.format(now);
    const currentHour = parseInt(hourFormatter.format(now));
    return { todayStr, currentHour };
  };

  const { todayStr, currentHour } = getCurrentTimeInfo();
  const todayIndex = weekDates.indexOf(todayStr);
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const currentHourRef = useRef<HTMLDivElement>(null);

  // Track scroll position for fade indicators
  const [canScrollUp, setCanScrollUp] = useState(false);
  const [canScrollDown, setCanScrollDown] = useState(true);

  const handleScroll = () => {
    if (!scrollContainerRef.current) return;
    const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef.current;
    setCanScrollUp(scrollTop > 10);
    setCanScrollDown(scrollTop < scrollHeight - clientHeight - 10);
  };

  // Initialize scroll indicators after content loads
  useEffect(() => {
    const timer = setTimeout(handleScroll, 150);
    return () => clearTimeout(timer);
  }, [slots.length, selectedParticipants.length, isLoading]);

  // Auto-scroll to position current hour as 3rd visible row
  useEffect(() => {
    // Only scroll when we have content to show
    if (selectedParticipants.length === 0 || isLoading) return;

    // Small delay to ensure DOM is ready after render
    const timer = setTimeout(() => {
      if (!scrollContainerRef.current) return;

      const rowHeight = 28; // h-6 (24px) + gap (4px)
      const rowsAbove = 4; // Show a few slots before current time

      // Calculate which slot should be at the top (multiply by slots per hour)
      const targetHour = todayIndex >= 0
        ? Math.max(0, currentHour - Math.floor(rowsAbove / SLOTS_PER_HOUR))
        : 7; // Default to 7am for other weeks

      scrollContainerRef.current.scrollTop = targetHour * SLOTS_PER_HOUR * rowHeight;
    }, 100);

    return () => clearTimeout(timer);
  }, [weekOffset, todayIndex, currentHour, selectedParticipants.length, isLoading, slots.length]);

  // Find a slot that matches the given display timezone date/hour/minute
  const getSlot = (dateStr: string, hour: number, minute: number): TimeSlot | undefined => {
    // Convert display timezone date/hour/minute to UTC
    const targetUTC = toUTCDate(dateStr, hour, minute, displayTimezone);

    return slots.find((s) => {
      const slotDate = new Date(s.start_time);
      // Compare UTC timestamps (with some tolerance for rounding)
      return Math.abs(slotDate.getTime() - targetUTC.getTime()) < 60000; // 1 minute tolerance
    });
  };

  const getEffectiveAvailability = (slot: TimeSlot) => {
    if (slot.availability === 'partial' && !showPartialAvailability) {
      return 'none';
    }
    return slot.availability;
  };

  const formatTime = (hour: number, minute: number) => {
    return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
  };

  const isSlotTooSoon = (dateStr: string, hour: number, minute: number) => {
    // Convert to UTC and compare with current time
    const slotTimeUTC = toUTCDate(dateStr, hour, minute, displayTimezone);
    const now = new Date();
    const twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);
    return slotTimeUTC < twoHoursFromNow;
  };

  const getWeekDateRange = () => {
    if (weekDates.length < 5) return '';
    const monday = new Date(weekDates[0] + 'T12:00:00Z');
    const friday = new Date(weekDates[4] + 'T12:00:00Z');

    const format = (d: Date) =>
      d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'UTC' });
    return `${format(monday)} – ${format(friday)}`;
  };

  // Format time for display in popover (in the display timezone)
  const formatDisplayTime = (hour: number, minute: number) => {
    // Create a date at that time
    const date = new Date();
    date.setHours(hour, minute, 0, 0);
    return new Intl.DateTimeFormat('en-US', {
      hour: 'numeric',
      minute: '2-digit',
      hour12: true,
    }).format(date);
  };

  if (selectedParticipants.length === 0) {
    return (
      <div className="bg-card rounded-xl shadow-card p-8 text-center animate-fade-in">
        <div className="text-muted-foreground">
          <p className="text-lg font-medium mb-2">Select participants to view availability</p>
          <p className="text-sm">Add people from the search above to see their common free times</p>
        </div>
      </div>
    );
  }

  if (isLoading) {
    return (
      <div className="bg-card rounded-xl shadow-card p-8 text-center animate-fade-in">
        <Loader2 className="w-8 h-8 mx-auto mb-4 animate-spin text-primary" />
        <p className="text-muted-foreground">Loading availability...</p>
      </div>
    );
  }

  return (
    <div className="bg-card rounded-xl shadow-card p-6 animate-slide-up">
      <div className="mb-6 flex justify-between items-start">
        <div>
          <div className="flex items-center gap-2">
            <h3 className="text-lg font-semibold text-foreground">
              Common Availability — Week of {getWeekDateRange()}
            </h3>
          </div>
          <p className="text-sm text-muted-foreground mt-1">
            {selectedParticipants.length} participant{selectedParticipants.length > 1 ? 's' : ''}: {selectedParticipants.map(p => p.name.split(' ')[0]).join(', ')}
            <span className="mx-2">•</span>
            <span className="text-primary font-medium">
              {`Times in ${formatTimezoneDisplay(displayTimezone)}`}
            </span>
          </p>
        </div>
        {onWeekOffsetChange && (
          <div className="flex items-center gap-1">
            <Button
              variant="ghost"
              size="icon"
              className="h-8 w-8"
              disabled={weekOffset <= MIN_WEEK_OFFSET}
              onClick={() => onWeekOffsetChange(weekOffset - 1)}
            >
              <ChevronLeft className="w-4 h-4" />
            </Button>
            {weekOffset !== 0 && (
              <Button
                variant="ghost"
                size="sm"
                className="h-8 text-xs"
                onClick={() => onWeekOffsetChange(0)}
              >
                This week
              </Button>
            )}
            {weekOffset < maxWeekOffset ? (
              <Button
                variant="ghost"
                size="icon"
                className="h-8 w-8"
                onClick={() => onWeekOffsetChange(weekOffset + 1)}
              >
                <ChevronRight className="w-4 h-4" />
              </Button>
            ) : !expanded ? (
              <Button
                variant="outline"
                size="sm"
                className="h-8 text-xs gap-1"
                onClick={() => setExpanded(true)}
              >
                <ChevronsRight className="w-3.5 h-3.5" />
                Look further ahead
              </Button>
            ) : (
              <Button
                variant="ghost"
                size="icon"
                className="h-8 w-8"
                disabled
              >
                <ChevronRight className="w-4 h-4" />
              </Button>
            )}
          </div>
        )}
      </div>

      <div className="relative">
        {/* Scroll fade indicator - top */}
        <div
          className={cn(
            "absolute top-0 left-0 right-0 h-8 bg-gradient-to-b from-card to-transparent z-20 pointer-events-none transition-opacity duration-200",
            canScrollUp ? "opacity-100" : "opacity-0"
          )}
        />
        {/* Scroll fade indicator - bottom */}
        <div
          className={cn(
            "absolute bottom-0 left-0 right-0 h-12 bg-gradient-to-t from-card via-card/80 to-transparent z-20 pointer-events-none transition-opacity duration-200 flex items-end justify-center pb-2",
            canScrollDown ? "opacity-100" : "opacity-0"
          )}
        >
          <span className="text-xs text-muted-foreground animate-pulse">↓ Scroll for more hours</span>
        </div>

        <div
          className="overflow-x-auto overflow-y-auto max-h-[500px]"
          ref={scrollContainerRef}
          onScroll={handleScroll}
        >
          <div className="min-w-[600px]">
            <div className={cn(
              "grid gap-1 mb-2 sticky top-0 bg-card z-10",
              showSecondaryTimezone
                ? "grid-cols-[50px_50px_repeat(5,1fr)]"
                : "grid-cols-[60px_repeat(5,1fr)]"
            )}>
              {showSecondaryTimezone ? (
                <>
                  <div className="text-center text-xs font-medium text-primary py-2">
                    {getTimezoneAbbrev(displayTimezone)}
                  </div>
                  <div className="text-center text-xs font-medium text-muted-foreground py-2">
                    {getTimezoneAbbrev(secondaryTimezone)}
                  </div>
                </>
              ) : (
                <div></div>
              )}
              {dayNames.map((dayName, i) => (
                <div
                  key={dayName}
                  className="text-center text-sm font-medium text-muted-foreground py-2"
                >
                  <div>{dayName}</div>
                  <div className="text-xs opacity-70">
                    {weekDates[i]?.slice(5).replace('-', '/')}
                  </div>
                </div>
              ))}
            </div>

            <div className="space-y-1">
              {timeSlots.map(({ hour, minute }) => (
                <div
                  key={`${hour}-${minute}`}
                  ref={todayIndex >= 0 && hour === currentHour && minute === 0 ? currentHourRef : undefined}
                  className={cn(
                    "grid gap-1",
                    showSecondaryTimezone
                      ? "grid-cols-[50px_50px_repeat(5,1fr)]"
                      : "grid-cols-[60px_repeat(5,1fr)]"
                  )}
                >
                  {showSecondaryTimezone ? (
                    <>
                      <div className="text-xs text-primary font-medium flex items-center justify-center gap-1">
                        {todayIndex >= 0 && hour === currentHour && minute === 0 && (
                          <span className="w-2 h-2 rounded-full bg-primary animate-pulse" />
                        )}
                        {formatTime(hour, minute)}
                      </div>
                      <div className="text-xs text-muted-foreground flex items-center justify-center">
                        {(() => {
                          const converted = convertTimeBetweenTimezones(hour, minute, weekDates[0] || '', displayTimezone, secondaryTimezone);
                          return formatTime(converted.hour, converted.minute);
                        })()}
                      </div>
                    </>
                  ) : (
                    <div className="text-xs text-muted-foreground flex items-center justify-end pr-3 gap-1">
                      {todayIndex >= 0 && hour === currentHour && minute === 0 && (
                        <span className="w-2 h-2 rounded-full bg-primary animate-pulse" />
                      )}
                      {formatTime(hour, minute)}
                    </div>
                  )}
                  {weekDates.map((dateStr, dayIndex) => {
                    const slot = getSlot(dateStr, hour, minute);
                    const dayName = dayNames[dayIndex];
                    const tooSoon = isSlotTooSoon(dateStr, hour, minute);
                    if (!slot) return <div key={`${dateStr}-${hour}-${minute}`} className="h-6 bg-muted rounded" />;

                    const effectiveAvailability = getEffectiveAvailability(slot);

                    // Calculate end time for display
                    const endMinute = minute + SLOT_INTERVAL_MINUTES;
                    const endHour = hour + Math.floor(endMinute / 60);
                    const endMinuteNormalized = endMinute % 60;

                    return (
                      <Popover key={`${dateStr}-${hour}-${minute}`}>
                        <PopoverTrigger asChild>
                          <button
                            className={cn(
                              "h-6 rounded-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
                              tooSoon && "opacity-40 cursor-not-allowed",
                              !tooSoon && "hover:scale-105 hover:shadow-md",
                              effectiveAvailability === 'full' && "bg-availability-full hover:bg-availability-full/90",
                              effectiveAvailability === 'partial' && "bg-availability-partial hover:bg-availability-partial/90",
                              effectiveAvailability === 'none' && "bg-availability-none hover:bg-availability-none/90"
                            )}
                          />
                        </PopoverTrigger>
                        <PopoverContent className="w-64 p-4 animate-scale-in" align="center">
                          <div className="space-y-3">
                            <div className="font-semibold text-foreground">
                              {dayName} {formatDisplayTime(hour, minute)}–{formatDisplayTime(endHour, endMinuteNormalized)}
                            </div>
                            {tooSoon && (
                              <div className="text-sm text-muted-foreground italic">
                                This time slot has passed or is too soon to schedule
                              </div>
                            )}
                            <div className="space-y-2">
                              {selectedParticipants.map((participant) => {
                                const isAvailable = slot.availableParticipants.includes(participant.name);

                                return (
                                  <div
                                    key={participant.id}
                                    className="flex items-center gap-2 text-sm"
                                  >
                                    {isAvailable ? (
                                      <Check className="w-4 h-4 text-availability-full" />
                                    ) : (
                                      <X className="w-4 h-4 text-destructive" />
                                    )}
                                    <span className={cn(
                                      isAvailable ? "text-foreground" : "text-muted-foreground"
                                    )}>
                                      {participant.name.split(' ')[0]}
                                    </span>
                                  </div>
                                );
                              })}
                            </div>
                            {effectiveAvailability !== 'none' && !tooSoon && (
                              <Button
                                variant="schedule"
                                className="w-full mt-2"
                                onClick={() => onSlotSelect(slot)}
                              >
                                Schedule
                              </Button>
                            )}
                          </div>
                        </PopoverContent>
                      </Popover>
                    );
                  })}
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>

      <div className="flex items-center justify-center gap-6 mt-6 pt-4 border-t border-border">
        <div className="flex items-center gap-2">
          <div className="w-4 h-4 rounded bg-availability-full"></div>
          <span className="text-xs text-muted-foreground">All free</span>
        </div>
        {showPartialAvailability && (
          <div className="flex items-center gap-2">
            <div className="w-4 h-4 rounded bg-availability-partial"></div>
            <span className="text-xs text-muted-foreground">Partial</span>
          </div>
        )}
        <div className="flex items-center gap-2">
          <div className="w-4 h-4 rounded bg-availability-none"></div>
          <span className="text-xs text-muted-foreground">No overlap</span>
        </div>
      </div>
    </div>
  );
};
