"use client";

import { useEffect, useMemo, useState } from "react";

export type WeeklyAvailabilityRange = {
  dayOfWeek: number;
  startTime: string;
  endTime: string;
};

type WeeklyAvailabilityGridProps = {
  value: WeeklyAvailabilityRange[];
  onChange: (next: WeeklyAvailabilityRange[]) => void;
  blockedRanges?: WeeklyAvailabilityRange[];
  selectedLabel: string;
  openLabel: string;
  blockedLabel: string;
  instructionsId?: string;
};

const DAY_LABELS = ["Dom", "Lun", "Mar", "Mie", "Jue", "Vie", "Sab"];
const DAY_FULL_LABELS = ["domingo", "lunes", "martes", "miercoles", "jueves", "viernes", "sabado"];
const HALF_HOUR_SLOTS = Array.from({ length: 47 }, (_, index) => {
  const hours = Math.floor(index / 2);
  const minutes = index % 2 === 0 ? "00" : "30";
  return `${String(hours).padStart(2, "0")}:${minutes}`;
});

function timeToMinutes(value: string) {
  const [hours, minutes] = value.split(":").map((part) => Number(part));
  return hours * 60 + minutes;
}

function cellKey(dayOfWeek: number, startTime: string) {
  return `${dayOfWeek}|${startTime}`;
}

function expandRangesToCells(ranges: WeeklyAvailabilityRange[]) {
  const cells = new Set<string>();

  for (const range of ranges) {
    let minuteCursor = timeToMinutes(range.startTime);
    const endMinutes = timeToMinutes(range.endTime);

    while (minuteCursor < endMinutes) {
      const hour = Math.floor(minuteCursor / 60);
      const minute = minuteCursor % 60;
      cells.add(cellKey(range.dayOfWeek, `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`));
      minuteCursor += 30;
    }
  }

  return cells;
}

function compressCellsToRanges(cells: Set<string>) {
  const next: WeeklyAvailabilityRange[] = [];

  for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek += 1) {
    const daySlots = HALF_HOUR_SLOTS.filter((slot) => cells.has(cellKey(dayOfWeek, slot)));
    let index = 0;

    while (index < daySlots.length) {
      const startTime = daySlots[index];
      let endMinutes = timeToMinutes(startTime) + 30;
      let cursor = index + 1;

      while (cursor < daySlots.length && timeToMinutes(daySlots[cursor]) === endMinutes) {
        endMinutes += 30;
        cursor += 1;
      }

      next.push({
        dayOfWeek,
        startTime,
        endTime: `${String(Math.floor(endMinutes / 60)).padStart(2, "0")}:${String(endMinutes % 60).padStart(2, "0")}`,
      });
      index = cursor;
    }
  }

  return next;
}

export function clampWeeklyAvailabilityRanges(value: WeeklyAvailabilityRange[], allowedRanges: WeeklyAvailabilityRange[]) {
  const allowedCells = expandRangesToCells(allowedRanges);
  const nextCells = new Set<string>();

  for (const key of expandRangesToCells(value)) {
    if (allowedCells.has(key)) {
      nextCells.add(key);
    }
  }

  return compressCellsToRanges(nextCells);
}

function formatSlotLabel(value: string) {
  return value;
}

function getCellAriaLabel(dayOfWeek: number, slot: string, stateLabel: string) {
  return `${DAY_FULL_LABELS[dayOfWeek] ?? "dia"} ${slot}, ${stateLabel}`;
}

export function WeeklyAvailabilityGrid({
  value,
  onChange,
  blockedRanges = [],
  selectedLabel,
  openLabel,
  blockedLabel,
  instructionsId,
}: WeeklyAvailabilityGridProps) {
  const selectedCells = useMemo(() => expandRangesToCells(value), [value]);
  const blockedCells = useMemo(() => expandRangesToCells(blockedRanges), [blockedRanges]);
  const [dragMode, setDragMode] = useState<boolean | null>(null);

  useEffect(() => {
    function clearDragMode() {
      setDragMode(null);
    }

    window.addEventListener("mouseup", clearDragMode);
    return () => window.removeEventListener("mouseup", clearDragMode);
  }, []);

  function updateCell(dayOfWeek: number, slot: string, nextValue: boolean) {
    const nextCells = new Set(selectedCells);
    const key = cellKey(dayOfWeek, slot);

    if (nextValue) {
      nextCells.add(key);
    } else {
      nextCells.delete(key);
    }

    onChange(compressCellsToRanges(nextCells));
  }

  return (
    <div className="overflow-x-auto">
      <div className="inline-block min-w-full">
        <p id={instructionsId} className="mb-4 text-xs text-white/55">
          Arrastra para marcar bloques de 30 minutos. Tambien puedes usar Tab y Enter o Espacio sobre cada bloque.
        </p>

        <div className="grid grid-cols-[64px_repeat(7,minmax(80px,1fr))] gap-2 text-xs text-white/55">
          <div />
          {DAY_LABELS.map((label) => (
            <div key={label} className="px-2 pb-1 text-center uppercase tracking-[0.18em]">
              {label}
            </div>
          ))}
        </div>

        <div className="mt-2 space-y-1">
          {HALF_HOUR_SLOTS.map((slot) => (
            <div key={slot} className="grid grid-cols-[64px_repeat(7,minmax(80px,1fr))] gap-2">
              <div className="pt-2 text-[11px] text-white/35">{formatSlotLabel(slot)}</div>
              {DAY_LABELS.map((_label, dayOfWeek) => {
                const key = cellKey(dayOfWeek, slot);
                const isBlocked = blockedRanges.length > 0 && !blockedCells.has(key);
                const isSelected = selectedCells.has(key);

                return (
                  <button
                    key={key}
                    type="button"
                    aria-pressed={isSelected}
                    aria-disabled={isBlocked}
                    aria-label={getCellAriaLabel(dayOfWeek, slot, isBlocked ? blockedLabel : isSelected ? selectedLabel : openLabel)}
                    aria-describedby={instructionsId}
                    title={isBlocked ? blockedLabel : isSelected ? selectedLabel : openLabel}
                    className={`h-8 rounded-md border text-[10px] transition ${
                      isBlocked
                        ? "cursor-not-allowed border-red-500/25 bg-red-500/15 text-red-100/80"
                        : isSelected
                          ? "border-[#c4d600]/40 bg-[#c4d600]/20 text-white"
                          : "border-white/10 bg-black/25 text-white/45 hover:border-white/20 hover:text-white"
                    }`}
                    onClick={() => {
                      if (isBlocked) return;
                      updateCell(dayOfWeek, slot, !isSelected);
                    }}
                    onMouseDown={() => {
                      if (isBlocked) return;
                      const nextValue = !isSelected;
                      setDragMode(nextValue);
                      updateCell(dayOfWeek, slot, nextValue);
                    }}
                    onMouseEnter={() => {
                      if (isBlocked || dragMode === null) return;
                      if (dragMode !== isSelected) {
                        updateCell(dayOfWeek, slot, dragMode);
                      }
                    }}
                  >
                    <span className="sr-only">{isBlocked ? blockedLabel : isSelected ? selectedLabel : openLabel}</span>
                  </button>
                );
              })}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}
