'use client';

import { useMemo, useState } from 'react';
import { useLocale, useTranslations } from 'next-intl';
import type { AppointmentColumnProvider, AppointmentRecord } from '@/types/appointment';
import {
  SCHEDULE_SLOT_MINUTES,
  appointmentWithinWorkingHours,
  formatMinuteLabel,
  generateHourLabelsInRange,
  generateSlotStarts,
  isSlotWithinWorkingBlocks,
  snapRangeToSlots,
  unionDayBlockRange,
} from '@/components/staff/workingHours';
import {
  computeAppointmentLaneLayouts,
  findOverlapCluster,
  lanePositionStyles,
} from '@/components/appointments/appointmentOverlapLayout';
import { purposeBannerStyle } from '@/components/appointments/appointmentPurposeStyles';
import { AppointmentOverlapPopover } from '@/components/ui/appointments/AppointmentOverlapPopover';
import { formatMobileForDisplay } from '@/lib/phone';
import { startOfLocalDay } from '@/components/appointments/appointmentTime';
import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';

const HOUR_PX = 80;
const SLOT_PX = (HOUR_PX * SCHEDULE_SLOT_MINUTES) / 60;

function layoutBlockInRange(
  apt: AppointmentRecord,
  day: Date,
  rangeStartMinute: number,
  rangeEndMinute: number,
): { top: string; height: string } | null {
  const dayStart = startOfLocalDay(day);
  const rangeStartMs = dayStart.getTime() + rangeStartMinute * 60_000;
  const rangeEndMs = dayStart.getTime() + rangeEndMinute * 60_000;
  const start = new Date(apt.startAt);
  const end = new Date(apt.endAt);
  const clipStart = Math.max(start.getTime(), rangeStartMs);
  const clipEnd = Math.min(end.getTime(), rangeEndMs);
  if (clipEnd <= clipStart) {
    return null;
  }
  const rangeMs = rangeEndMs - rangeStartMs;
  const top = ((clipStart - rangeStartMs) / rangeMs) * 100;
  const height = ((clipEnd - clipStart) / rangeMs) * 100;
  return { top: `${top}%`, height: `${height}%` };
}

function appointmentDurationMinutes(apt: AppointmentRecord): number {
  const start = new Date(apt.startAt).getTime();
  const end = new Date(apt.endAt).getTime();
  return Math.max(0, Math.round((end - start) / 60_000));
}

function appointmentBannerHeightPx(durationMin: number, rangeMinutes: number, gridHeight: number): number {
  return (durationMin / rangeMinutes) * gridHeight;
}

function shortBannerNameClass(durationMin: number, rangeMinutes: number, gridHeight: number): string {
  const heightPx = appointmentBannerHeightPx(durationMin, rangeMinutes, gridHeight);
  if (heightPx < 18) {
    return 'text-[8px] leading-none';
  }
  if (durationMin < 60) {
    return 'text-[9px] leading-none';
  }
  return 'text-[11px] leading-tight';
}

type OverlapPopoverState = {
  appointments: AppointmentRecord[];
  anchorRect: DOMRect;
};

interface AppointmentScheduleGridProps {
  day: Date;
  providers: AppointmentColumnProvider[];
  appointments: AppointmentRecord[];
  treatmentCatalog: TreatmentCatalogEntry[];
  canBook: boolean;
  onSlotClick: (startMinute: number, providerUserId: string, providerName: string) => void;
  onAppointmentClick?: (appointment: AppointmentRecord) => void;
  onAppointmentOutsideHours?: (appointment: AppointmentRecord) => void;
}

export function AppointmentScheduleGrid({
  day,
  providers,
  appointments,
  treatmentCatalog,
  canBook,
  onSlotClick,
  onAppointmentClick,
  onAppointmentOutsideHours,
}: AppointmentScheduleGridProps) {
  const locale = useLocale();
  const t = useTranslations('appointments');
  const [overlapPopover, setOverlapPopover] = useState<OverlapPopoverState | null>(null);

  const visibleRange = useMemo(() => {
    const activeDayBlocks = providers
      .filter((p) => p.hasWorkingHours && p.dayBlocks.length > 0)
      .map((p) => p.dayBlocks);
    return unionDayBlockRange(activeDayBlocks);
  }, [providers]);

  const snappedRange = useMemo(() => {
    if (!visibleRange) return null;
    return snapRangeToSlots(visibleRange.startMinute, visibleRange.endMinute, SCHEDULE_SLOT_MINUTES);
  }, [visibleRange]);

  const slotStarts = useMemo(() => {
    if (!snappedRange) return [];
    return generateSlotStarts(
      snappedRange.startMinute,
      snappedRange.endMinute,
      SCHEDULE_SLOT_MINUTES,
    );
  }, [snappedRange]);

  const hourLabels = useMemo(() => {
    if (!snappedRange) return [];
    return generateHourLabelsInRange(snappedRange.startMinute, snappedRange.endMinute);
  }, [snappedRange]);

  const gridHeight = slotStarts.length * SLOT_PX;
  const rangeMinutes = snappedRange ? snappedRange.endMinute - snappedRange.startMinute : 0;

  const laneLayoutsByProvider = useMemo(() => {
    const map = new Map<string, ReturnType<typeof computeAppointmentLaneLayouts>>();
    for (const provider of providers) {
      const providerApts = appointments.filter((a) => a.providerUserId === provider.userId);
      map.set(provider.userId, computeAppointmentLaneLayouts(providerApts));
    }
    return map;
  }, [appointments, providers]);

  function handleAppointmentBannerClick(
    apt: AppointmentRecord,
    provider: AppointmentColumnProvider,
    providerAppointments: AppointmentRecord[],
    anchor: HTMLElement,
  ) {
    if (
      provider.hasWorkingHours &&
      provider.dayBlocks.length > 0 &&
      !appointmentWithinWorkingHours(new Date(apt.startAt), new Date(apt.endAt), provider.dayBlocks)
    ) {
      onAppointmentOutsideHours?.(apt);
      return;
    }

    const cluster = findOverlapCluster(apt.id, providerAppointments);
    if (cluster.length > 1) {
      setOverlapPopover({
        appointments: cluster,
        anchorRect: anchor.getBoundingClientRect(),
      });
      return;
    }
    if (!canBook) {
      return;
    }
    onAppointmentClick?.(apt);
  }

  if (providers.length === 0) {
    return (
      <div className="surface-card p-6 text-sm text-text-muted">{t('noProviders')}</div>
    );
  }

  if (!snappedRange || slotStarts.length === 0) {
    return (
      <div className="surface-card p-6 text-sm text-text-muted">{t('noWorkingHours')}</div>
    );
  }

  return (
    <>
      <div className="surface-card overflow-x-auto">
        <div className="min-w-[640px]">
          <div className="flex border-b border-border">
            <div className="w-14 flex-shrink-0" />
            {providers.map((p) => (
              <div
                key={p.userId}
                className="flex-1 min-w-[130px] text-center text-sm font-medium text-text-primary py-2.5 px-1 border-l border-border"
              >
                {p.name}
                {!p.hasWorkingHours && (
                  <span className="block text-[10px] font-normal text-text-muted mt-0.5">
                    {t('noHoursSet')}
                  </span>
                )}
                {p.hasWorkingHours && p.dayBlocks.length === 0 && (
                  <span className="block text-[10px] font-normal text-text-muted mt-0.5">
                    {t('offToday')}
                  </span>
                )}
              </div>
            ))}
          </div>

          <div className="flex">
            <div
              className="w-14 flex-shrink-0 border-r border-border bg-background-secondary/40 relative"
              style={{ height: gridHeight }}
            >
              {hourLabels.map((hour) => {
                const top = ((hour * 60 - snappedRange.startMinute) / rangeMinutes) * gridHeight;
                const height = (60 / rangeMinutes) * gridHeight;
                return (
                  <div
                    key={hour}
                    className="absolute left-0 right-0 text-[11px] text-text-muted flex items-start justify-end pr-1.5 pt-0.5 border-b border-border/50"
                    style={{ top, height }}
                  >
                    {formatMinuteLabel(hour * 60, locale)}
                  </div>
                );
              })}
            </div>

            <div className="flex-1 flex min-w-0">
              {providers.map((p) => {
                const providerAppointments = appointments.filter(
                  (a) => a.providerUserId === p.userId,
                );
                const laneLayouts = laneLayoutsByProvider.get(p.userId) ?? new Map();
                const columnFullyDisabled = !p.hasWorkingHours || p.dayBlocks.length === 0;

                return (
                  <div
                    key={p.userId}
                    className="flex-1 min-w-[130px] border-l border-border relative"
                    style={{ height: gridHeight }}
                  >
                    {slotStarts.map((slotStartMinute, index) => {
                      const slotActive =
                        !columnFullyDisabled &&
                        isSlotWithinWorkingBlocks(
                          slotStartMinute,
                          SCHEDULE_SLOT_MINUTES,
                          p.dayBlocks,
                        );
                      const slotDisabled = !canBook || columnFullyDisabled || !slotActive;

                      return (
                        <button
                          key={`${p.userId}-${slotStartMinute}`}
                          type="button"
                          disabled={slotDisabled}
                          title={
                            columnFullyDisabled
                              ? p.hasWorkingHours
                                ? t('slotOffToday')
                                : t('slotHoursNotConfigured')
                              : !slotActive
                                ? t('slotOutsideHours')
                                : slotDisabled
                                  ? t('slotCannotCreate')
                                  : t('slotBookAt', {
                                      time: formatMinuteLabel(slotStartMinute, locale),
                                    })
                          }
                          className={`absolute left-0 right-0 border-b border-border/50 transition-colors ${
                            slotDisabled
                              ? 'cursor-not-allowed bg-background-secondary/35 opacity-60'
                              : 'hover:bg-primary/8 cursor-pointer'
                          }`}
                          style={{ top: index * SLOT_PX, height: SLOT_PX }}
                          onClick={() => onSlotClick(slotStartMinute, p.userId, p.name)}
                        />
                      );
                    })}

                    {providerAppointments.map((apt) => {
                      const pos = layoutBlockInRange(
                        apt,
                        day,
                        snappedRange.startMinute,
                        snappedRange.endMinute,
                      );
                      if (!pos) {
                        return null;
                      }
                      const lane = laneLayouts.get(apt.id) ?? { lane: 0, laneCount: 1 };
                      const lanePos = lanePositionStyles(lane.lane, lane.laneCount);
                      const durationMin = appointmentDurationMinutes(apt);
                      const clusterSize = findOverlapCluster(apt.id, providerAppointments).length;
                      const isUnderOneHour = durationMin < 60;
                      const outsideHours =
                        p.hasWorkingHours &&
                        p.dayBlocks.length > 0 &&
                        !appointmentWithinWorkingHours(
                          new Date(apt.startAt),
                          new Date(apt.endAt),
                          p.dayBlocks,
                        );
                      const patientName = `${apt.patient.firstName} ${apt.patient.lastName}`;
                      const bannerTitle = [
                        patientName,
                        outsideHours ? t('outsideHoursBlocked') : null,
                        clusterSize > 1
                          ? t('overlappingChoose', { count: clusterSize })
                          : null,
                        !isUnderOneHour && apt.patient.mobile
                          ? formatMobileForDisplay(apt.patient.mobile)
                          : null,
                      ]
                        .filter(Boolean)
                        .join(' · ');

                      return (
                        <button
                          type="button"
                          key={apt.id}
                          onClick={(e) =>
                            handleAppointmentBannerClick(
                              apt,
                              p,
                              providerAppointments,
                              e.currentTarget,
                            )
                          }
                          className={`absolute min-h-0 overflow-hidden rounded-[var(--radius-sm)] border pointer-events-auto z-10 flex text-center focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/35 ${
                            outsideHours ? 'opacity-70 ring-1 ring-amber-500/60' : ''
                          } ${
                            isUnderOneHour
                              ? 'items-center justify-center px-0.5 py-0'
                              : 'flex-col items-center justify-center gap-0.5 px-1 py-0.5'
                          }`}
                          style={{
                            top: pos.top,
                            height: pos.height,
                            left: lanePos.left,
                            width: lanePos.width,
                            ...purposeBannerStyle(apt.purpose, treatmentCatalog),
                          }}
                          title={bannerTitle}
                        >
                          <span
                            className={`block w-full truncate pointer-events-none text-center font-medium ${shortBannerNameClass(durationMin, rangeMinutes, gridHeight)}`}
                          >
                            {patientName}
                          </span>
                          {!isUnderOneHour &&
                            apt.patient.mobile &&
                            lane.laneCount === 1 && (
                              <span className="block w-full truncate pointer-events-none text-center text-[10px] leading-tight opacity-90">
                                {formatMobileForDisplay(apt.patient.mobile)}
                              </span>
                            )}
                          {!isUnderOneHour && clusterSize > 1 && (
                            <span className="block w-full truncate pointer-events-none text-center text-[9px] leading-tight opacity-75">
                              {t('overlapping', { count: clusterSize })}
                            </span>
                          )}
                        </button>
                      );
                    })}
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      </div>

      {overlapPopover && (
        <AppointmentOverlapPopover
          appointments={overlapPopover.appointments}
          treatmentCatalog={treatmentCatalog}
          anchorRect={overlapPopover.anchorRect}
          onSelect={(apt) => {
            if (!canBook) {
              setOverlapPopover(null);
              return;
            }
            const provider = providers.find((p) => p.userId === apt.providerUserId);
            if (
              provider &&
              provider.hasWorkingHours &&
              provider.dayBlocks.length > 0 &&
              !appointmentWithinWorkingHours(
                new Date(apt.startAt),
                new Date(apt.endAt),
                provider.dayBlocks,
              )
            ) {
              onAppointmentOutsideHours?.(apt);
              setOverlapPopover(null);
              return;
            }
            onAppointmentClick?.(apt);
          }}
          onClose={() => setOverlapPopover(null)}
        />
      )}
    </>
  );
}
