import { useEffect, useRef, useState } from 'react';
import {
  format, startOfMonth, endOfMonth, startOfWeek, endOfWeek,
  eachDayOfInterval, isSameMonth, addMonths, subMonths,
  isSameDay, differenceInDays, parseISO, isWithinInterval
} from 'date-fns';
import { es } from 'date-fns/locale/es';
import { ChevronLeft, ChevronRight, Users, Moon } from 'lucide-react';
import type { Reservation } from '../types';
import { useProperty } from '../contexts/PropertyContext';
import { PROPERTY_CONFIG } from '@naturcalabacera/shared';
import { ServiceIcons } from './ServiceIcons';

interface Props {
  reservations: Reservation[];
  onSelectDay: (day: Date) => void;
  onSelectReservation: (reservation: Reservation) => void;
  onSelectRange?: (start: Date, end: Date) => void;
  isLoading?: boolean;
  viewerMode?: boolean;
}

export function CalendarGrid({
  reservations,
  onSelectDay,
  onSelectReservation,
  onSelectRange,
  isLoading: _isLoading = false,
  viewerMode = false,
}: Props) {
  const { property } = useProperty();
  const propertyConfig = PROPERTY_CONFIG[property];
  const [currentDate, setCurrentDate] = useState(new Date());

  // Drag-to-select state
  const [dragStart, setDragStart] = useState<Date | null>(null);
  const [dragEnd, setDragEnd] = useState<Date | null>(null);
  const [isDragging, setIsDragging] = useState(false);
  const gridBodyRef = useRef<HTMLDivElement>(null);
  // Tracks mouse movement to distinguish click vs drag
  const mouseMoved = useRef(false);
  // Prevents the click event that fires after mouseUp from triggering onSelectDay
  const dragJustFinished = useRef(false);

  const monthStart = startOfMonth(currentDate);
  const monthEnd = endOfMonth(monthStart);
  const calendarStart = startOfWeek(monthStart, { weekStartsOn: 0 });
  const calendarEnd = endOfWeek(monthEnd, { weekStartsOn: 0 });

  const calendarDays = eachDayOfInterval({ start: calendarStart, end: calendarEnd });

  const weeks: Date[][] = [];
  for (let i = 0; i < calendarDays.length; i += 7) {
    weeks.push(calendarDays.slice(i, i + 7));
  }

  const nextMonth = () => setCurrentDate(addMonths(currentDate, 1));
  const prevMonth = () => setCurrentDate(subMonths(currentDate, 1));

  // Display occupancy: a day is occupied for rendering if within [start, end] inclusive
  const isDayOccupied = (day: Date): boolean => {
    return reservations.some(res => {
      const s = parseISO(res.start_date);
      const e = parseISO(res.end_date);
      return isWithinInterval(day, { start: s, end: e }) || isSameDay(day, s) || isSameDay(day, e);
    });
  };

  // Get reservation for a specific day (for click-through on occupied cells)
  const getReservationForDay = (day: Date): Reservation | undefined => {
    return reservations.find(res => {
      const s = parseISO(res.start_date);
      const e = parseISO(res.end_date);
      return isWithinInterval(day, { start: s, end: e }) || isSameDay(day, s) || isSameDay(day, e);
    });
  };

  // Calculate which calendar day is under a given coordinate
  const getDayFromCoords = (clientX: number, clientY: number): Date | null => {
    const el = gridBodyRef.current;
    if (!el) return null;
    const rect = el.getBoundingClientRect();
    const x = clientX - rect.left;
    const y = clientY - rect.top;
    if (x < 0 || x >= rect.width || y < 0 || y >= rect.height) return null;
    const col = Math.min(Math.floor(x / (rect.width / 7)), 6);
    const row = Math.min(Math.floor(y / (rect.height / weeks.length)), weeks.length - 1);
    return weeks[row]?.[col] ?? null;
  };

  // Is a day inside the current drag selection range?
  const isInDragSelection = (day: Date): boolean => {
    if (!dragStart || !dragEnd) return false;
    const start = dragStart <= dragEnd ? dragStart : dragEnd;
    const end = dragStart <= dragEnd ? dragEnd : dragStart;
    return day >= start && day <= end;
  };

  // Confirm drag and open modal
  const confirmDrag = (start: Date, end: Date) => {
    const s = start <= end ? start : end;
    const e = start <= end ? end : start;
    if (isSameDay(s, e)) {
      onSelectDay(s);
    } else if (onSelectRange) {
      onSelectRange(s, e);
    } else {
      onSelectDay(s);
    }
  };

  // --- Grid body mouse events ---

  const handleGridMouseDown = (ev: React.MouseEvent) => {
    if (viewerMode) return;
    const day = getDayFromCoords(ev.clientX, ev.clientY);
    if (!day) return;
    // Allow drag start on any day (free or occupied boundary)
    // Drag is only initiated on non-occupied days
    if (isDayOccupied(day)) return;
    ev.preventDefault();
    mouseMoved.current = false;
    setDragStart(day);
    setDragEnd(day);
    setIsDragging(true);
  };

  const handleGridMouseMove = (ev: React.MouseEvent) => {
    if (!isDragging || !dragStart) return;
    mouseMoved.current = true;
    const day = getDayFromCoords(ev.clientX, ev.clientY);
    if (day) setDragEnd(day);
  };

  const handleGridMouseUp = (ev: React.MouseEvent) => {
    if (!isDragging || !dragStart || !dragEnd) return;
    const hasMoved = mouseMoved.current;
    const start = dragStart;
    const end = dragEnd;
    setIsDragging(false);
    setDragStart(null);
    setDragEnd(null);
    if (hasMoved && !isSameDay(start, end)) {
      dragJustFinished.current = true;
      confirmDrag(start, end);
    }
    // Single clicks are handled entirely by handleGridClick
    ev.stopPropagation();
  };

  // Handle click on grid body (for free-day single clicks when drag is not involved)
  const handleGridClick = (ev: React.MouseEvent) => {
    if (viewerMode || isDragging) return;
    // Ignore the synthetic click that fires immediately after a drag ends
    if (dragJustFinished.current) {
      dragJustFinished.current = false;
      return;
    }
    const day = getDayFromCoords(ev.clientX, ev.clientY);
    if (!day) return;
    const res = getReservationForDay(day);
    if (res) {
      // Click on occupied day — open reservation
      onSelectReservation(res);
    } else {
      // Click on free day — open create modal
      onSelectDay(day);
    }
  };

  // --- Touch events (mobile drag) ---

  const handleTouchStart = (ev: React.TouchEvent) => {
    if (viewerMode) return;
    const touch = ev.touches[0];
    const day = getDayFromCoords(touch.clientX, touch.clientY);
    if (!day || isDayOccupied(day)) return;
    mouseMoved.current = false;
    setDragStart(day);
    setDragEnd(day);
    setIsDragging(true);
  };

  const handleTouchMove = (ev: React.TouchEvent) => {
    if (!isDragging) return;
    mouseMoved.current = true;
    const touch = ev.touches[0];
    const day = getDayFromCoords(touch.clientX, touch.clientY);
    if (day) setDragEnd(day);
  };

  const handleTouchEnd = () => {
    if (!isDragging || !dragStart || !dragEnd) return;
    const hasMoved = mouseMoved.current;
    const start = dragStart;
    const end = dragEnd;
    setIsDragging(false);
    setDragStart(null);
    setDragEnd(null);
    if (hasMoved && !isSameDay(start, end)) {
      dragJustFinished.current = true;
      confirmDrag(start, end);
    } else {
      onSelectDay(start);
    }
  };

  // Cancel drag if mouse leaves the window
  useEffect(() => {
    const cancel = () => {
      if (isDragging) {
        setIsDragging(false);
        setDragStart(null);
        setDragEnd(null);
      }
    };
    document.addEventListener('mouseup', cancel);
    return () => document.removeEventListener('mouseup', cancel);
  }, [isDragging]);

  // --- Reservation blocks (visual only — clicks handled at grid body level) ---
  const renderReservationBlocks = () => {
    const blocks: React.ReactElement[] = [];

    reservations.forEach((res) => {
      const startDate = parseISO(res.start_date);
      const endDate = parseISO(res.end_date);

      const startIndex = calendarDays.findIndex(day => isSameDay(day, startDate));
      if (startIndex === -1) return;

      const endIndex = calendarDays.findIndex(day => isSameDay(day, endDate));
      if (endIndex === -1) return;

      const totalDuration = differenceInDays(endDate, startDate) + 1;
      const nights = totalDuration - 1;

      const isTeneriffa = res.origin === 'Teneriffa2000';
      const gradient = viewerMode
        ? 'bg-stone-400/30 dark:bg-stone-500/30'
        : isTeneriffa
          ? 'bg-blue-600/30 dark:bg-blue-500/30'
          : 'bg-yellow-500/30 dark:bg-yellow-400/30';

      const borderClass = viewerMode
        ? 'border-l-4 border-stone-400'
        : isTeneriffa
          ? 'border-l-4 border-blue-500'
          : 'border-l-4 border-yellow-500';
      const textShadow = 'drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]';

      let currentDayIndex = startIndex;
      let blockIndex = 0;

      while (currentDayIndex <= endIndex) {
        const weekIndex = Math.floor(currentDayIndex / 7);
        const dayOfWeek = currentDayIndex % 7;
        const daysUntilWeekEnd = 7 - dayOfWeek;
        const daysRemaining = endIndex - currentDayIndex + 1;
        const daysInThisWeek = Math.min(daysUntilWeekEnd, daysRemaining);
        const isFirstBlock = blockIndex === 0;

        blocks.push(
          <div
            key={`${res.id}-week-${weekIndex}`}
            className={`
              absolute pointer-events-none
              ${gradient} ${borderClass}
              z-10
              flex flex-col justify-end items-center md:items-start md:p-1.5
            `}
            style={{
              top: `calc(${weekIndex} * var(--cell-height))`,
              left: `${(dayOfWeek * 100 / 7)}%`,
              width: `${(daysInThisWeek * (100 / 7))}%`,
              height: 'var(--cell-height)',
            }}
          >
            {/* Desktop */}
            <div className="hidden md:block w-full">
              <div className={`text-xs font-black text-white truncate ${textShadow} px-1`}>
                {viewerMode ? 'Ocupado' : res.client_name}
              </div>
              {!viewerMode && isFirstBlock && (
                <div className="flex items-center gap-1.5 mt-0.5 px-1">
                  <div className={`flex items-center gap-0.5 text-white ${textShadow}`}>
                    <Moon className="w-2.5 h-2.5" />
                    <span className="text-[9px] font-bold">{nights}n</span>
                  </div>
                  <div className={`flex items-center gap-0.5 text-white ${textShadow}`}>
                    <Users className="w-2.5 h-2.5" />
                    <span className="text-[9px] font-bold">{res.adults_count + res.children_count}p</span>
                  </div>
                  <ServiceIcons reservation={res} />
                </div>
              )}
            </div>

            {/* Mobile */}
            <div className="md:hidden w-full flex items-end justify-between pb-0.5 px-0.5">
              {daysInThisWeek > 1 && (
                <span className={`text-[8px] font-black text-white/90 truncate uppercase tracking-tight ${textShadow}`}>
                  {viewerMode ? 'Ocupado' : res.client_name}
                </span>
              )}
              {!viewerMode && daysInThisWeek > 2 && isFirstBlock && (
                <ServiceIcons reservation={res} />
              )}
            </div>
          </div>
        );

        currentDayIndex += daysInThisWeek;
        blockIndex++;
      }
    });

    return blocks;
  };

  // Drag selection highlight blocks
  const renderDragSelection = () => {
    if (!isDragging || !dragStart || !dragEnd) return null;
    const start = dragStart <= dragEnd ? dragStart : dragEnd;
    const end = dragStart <= dragEnd ? dragEnd : dragStart;
    const startIndex = calendarDays.findIndex(d => d >= start);
    if (startIndex === -1) return null;
    const endIndex = calendarDays.findIndex(d => d >= end);
    const effectiveEnd = endIndex === -1 ? calendarDays.length - 1 : endIndex;

    const selBlocks: React.ReactElement[] = [];
    let cur = startIndex;
    while (cur <= effectiveEnd) {
      const weekIndex = Math.floor(cur / 7);
      const dayOfWeek = cur % 7;
      const daysUntilWeekEnd = 7 - dayOfWeek;
      const daysRemaining = effectiveEnd - cur + 1;
      const span = Math.min(daysUntilWeekEnd, daysRemaining);
      selBlocks.push(
        <div
          key={`sel-${cur}`}
          className="absolute z-20 pointer-events-none bg-emerald-500/20 border-l-2 border-r-2 border-emerald-400/60 border-t border-b border-emerald-400/30"
          style={{
            top: `calc(${weekIndex} * var(--cell-height))`,
            left: `${(dayOfWeek * 100 / 7)}%`,
            width: `${(span * (100 / 7))}%`,
            height: 'var(--cell-height)',
          }}
        />
      );
      cur += span;
    }
    return selBlocks;
  };

  // Days count label during drag
  const dragNights = dragStart && dragEnd
    ? Math.abs(differenceInDays(dragEnd, dragStart))
    : 0;

  return (
    <div className="bg-white dark:bg-emerald-950/10 rounded-3xl p-3 md:p-5 shadow-xl border border-stone-200 dark:border-emerald-900/30 backdrop-blur-xl transition-colors duration-500 flex flex-col">

      <style>{`
        :root {
            --cell-height: 2.75rem;
            --header-height: 1.75rem;
        }
        @media (min-width: 768px) {
            :root {
                --cell-height: 5rem;
                --header-height: 2.75rem;
            }
        }
      `}</style>

      {/* Header */}
      <div className="flex items-center justify-between mb-3 md:mb-4 shrink-0 overflow-visible">
        <div className="min-w-0 overflow-visible flex-1">
          <div className="flex items-baseline gap-3">
            <h2 className="text-xl md:text-2xl font-black text-transparent bg-gradient-to-r from-emerald-800 to-amber-600 dark:from-white dark:to-emerald-200 bg-clip-text capitalize pb-1 leading-tight">
              {format(currentDate, 'MMMM yyyy', { locale: es })}
            </h2>
            {/* Drag hint — shown only during active drag */}
            {isDragging && dragNights > 0 && (
              <span className="text-xs font-bold text-emerald-500 dark:text-emerald-400 animate-pulse">
                {dragNights} noche{dragNights !== 1 ? 's' : ''}
              </span>
            )}
          </div>
          <div className="flex items-center gap-2 mt-0.5">
            <p className="text-xs text-stone-500 dark:text-emerald-400/60 font-medium">
              {viewerMode ? 'Vista de disponibilidad' : 'Clic para crear · Arrastra para rango'}
            </p>
            <span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-gradient-to-r ${propertyConfig.color.gradient} text-white`}>
              {propertyConfig.label}
            </span>
          </div>
        </div>
        <div className="flex items-center gap-2 shrink-0 ml-3">
          <button
            onClick={prevMonth}
            className="p-2 rounded-xl bg-stone-100 dark:bg-emerald-900/20 hover:bg-emerald-50 dark:hover:bg-emerald-900/40 text-stone-600 dark:text-emerald-100/80 transition-all duration-300 shadow-sm hover:shadow-md hover:scale-110 border border-stone-200 dark:border-emerald-500/20"
          >
            <ChevronLeft className="w-4 h-4 md:w-5 md:h-5" />
          </button>
          <button
            onClick={nextMonth}
            className="p-2 rounded-xl bg-stone-100 dark:bg-emerald-900/20 hover:bg-emerald-50 dark:hover:bg-emerald-900/40 text-stone-600 dark:text-emerald-100/80 transition-all duration-300 shadow-sm hover:shadow-md hover:scale-110 border border-stone-200 dark:border-emerald-500/20"
          >
            <ChevronRight className="w-4 h-4 md:w-5 md:h-5" />
          </button>
        </div>
      </div>

      {/* Grid container */}
      <div className="bg-stone-50 dark:bg-black/20 backdrop-blur-xl rounded-2xl overflow-hidden border border-stone-200 dark:border-emerald-900/30 shadow-2xl shadow-stone-200/50 dark:shadow-black/20 relative">
        <div className="w-full flex flex-col">

          {/* Days header */}
          <div className="grid grid-cols-7 bg-stone-100 dark:bg-emerald-950/30 border-b border-stone-200 dark:border-emerald-900/30 backdrop-blur-sm shrink-0">
            {['D', 'L', 'M', 'X', 'J', 'V', 'S'].map((day, i) => (
              <div key={i} className="text-center py-1.5 md:py-3 text-[9px] md:text-xs font-black text-stone-400 dark:text-emerald-500/60 uppercase tracking-widest">
                <span className="md:hidden">{day}</span>
                <span className="hidden md:inline">{['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'][i]}</span>
              </div>
            ))}
          </div>

          {/* Grid body — handles all pointer interactions via coordinates */}
          <div
            ref={gridBodyRef}
            className={`relative ${!viewerMode ? (isDragging ? 'cursor-crosshair' : 'cursor-pointer') : 'cursor-default'}`}
            onMouseDown={handleGridMouseDown}
            onMouseMove={handleGridMouseMove}
            onMouseUp={handleGridMouseUp}
            onClick={handleGridClick}
            onTouchStart={handleTouchStart}
            onTouchMove={handleTouchMove}
            onTouchEnd={handleTouchEnd}
            // Prevent text selection during drag
            style={{ userSelect: isDragging ? 'none' : undefined }}
          >
            {/* Grid cells — purely visual, no pointer-events needed */}
            {weeks.map((week, weekIdx) => (
              <div key={weekIdx} className="grid grid-cols-7 border-b border-stone-200 dark:border-emerald-900/30 last:border-b-0">
                {week.map((day) => {
                  const isCurrentMonth = isSameMonth(day, monthStart);
                  const isOccupied = isDayOccupied(day);
                  const inSel = isInDragSelection(day);
                  const isToday = isSameDay(day, new Date());

                  return (
                    <div
                      key={day.toString()}
                      className={`
                        relative border-r border-stone-200 dark:border-emerald-900/30 last:border-r-0
                        p-1 md:p-2
                        ${!isCurrentMonth ? 'bg-stone-100/50 dark:bg-emerald-950/30' : ''}
                        ${inSel && !isOccupied ? 'bg-emerald-50/60 dark:bg-emerald-900/10' : ''}
                        ${isToday && isCurrentMonth ? 'bg-blue-50/60 dark:bg-blue-500/5' : ''}
                      `}
                      style={{ height: 'var(--cell-height)' }}
                    >
                      <span className={`
                        inline-flex items-center justify-center w-4 h-4 md:w-7 md:h-7 rounded-md text-[9px] md:text-xs font-bold
                        transition-all duration-200 relative z-30
                        ${isToday && isCurrentMonth ? 'bg-blue-500 text-white ring-2 ring-blue-400/60 ring-offset-1 ring-offset-white dark:ring-offset-emerald-950 shadow-lg shadow-blue-500/30' : ''}
                        ${!isToday && isCurrentMonth
                          ? isOccupied
                            ? 'text-white drop-shadow-md'
                            : inSel
                              ? 'text-emerald-700 dark:text-emerald-300'
                              : 'text-stone-700 dark:text-emerald-100/80'
                          : !isCurrentMonth ? 'text-stone-300 dark:text-emerald-900/40' : ''
                        }
                      `}>
                        {format(day, 'd')}
                      </span>
                    </div>
                  );
                })}
              </div>
            ))}

            {/* Reservation color blocks */}
            {renderReservationBlocks()}

            {/* Drag selection overlay */}
            {renderDragSelection()}
          </div>
        </div>
      </div>

      {/* Legend */}
      <div className="mt-3 flex flex-wrap items-center gap-3 md:gap-5 text-xs text-stone-500 dark:text-emerald-400/60 shrink-0">
        <div className="flex items-center gap-2 px-3 py-1.5 bg-blue-50 dark:bg-blue-500/5 rounded-xl border border-blue-200 dark:border-blue-500/10">
          <div className="w-3 h-3 rounded-md bg-gradient-to-br from-blue-400 to-blue-600 shadow-sm shadow-blue-500/50 shrink-0" />
          <span className="font-semibold text-stone-700 dark:text-emerald-100/80">Teneriffa</span>
        </div>
        <div className="flex items-center gap-2 px-3 py-1.5 bg-amber-50 dark:bg-amber-500/5 rounded-xl border border-amber-200 dark:border-amber-500/10">
          <div className="w-3 h-3 rounded-md bg-gradient-to-br from-amber-400 to-amber-600 shadow-sm shadow-amber-500/50 shrink-0" />
          <span className="font-semibold text-stone-700 dark:text-emerald-100/80">Natur</span>
        </div>
      </div>
    </div>
  );
}
