/**
 * WeekView - 7-day week grid (Mon-Sun) with hourly time slots.
 *
 * Click an empty slot -> fires `onCreateAt(date)`.
 * Click an entry -> fires `onEditEntry(entry)`.
 * Drag an entry onto another day/slot -> calls `onMoveEntry(entry, newStart)`.
 */

import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import clsx from 'clsx';
import type { CalendarEntry } from '@/api/calendar';

export interface WeekViewProps {
  /** Monday of the visible week. */
  visibleWeek: Date;
  entries: CalendarEntry[];
  loading?: boolean;
  onCreateAt: (date: Date) => void;
  onEditEntry: (entry: CalendarEntry) => void;
  onMoveEntry: (entry: CalendarEntry, newStart: Date) => void | Promise<void>;
}

const HOUR_START = 0;
const HOUR_END = 24;
const SLOT_HEIGHT = 48; // px per hour

function startOfDay(d: Date): Date {
  return new Date(d.getFullYear(), d.getMonth(), d.getDate());
}

function addDays(d: Date, n: number): Date {
  const out = new Date(d);
  out.setDate(out.getDate() + n);
  return out;
}

function sameDay(a: Date, b: Date): boolean {
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
}

function fmtTime(d: Date): string {
  const pad = (n: number) => String(n).padStart(2, '0');
  return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

export function WeekView({
  visibleWeek,
  entries,
  loading,
  onCreateAt,
  onEditEntry,
  onMoveEntry,
}: WeekViewProps) {
  const { t } = useTranslation();

  const today = startOfDay(new Date());
  const weekdays = t('calendar.weekdays', { returnObjects: true }) as string[];

  const weekDays = useMemo(() => {
    return Array.from({ length: 7 }, (_, i) => addDays(startOfDay(visibleWeek), i));
  }, [visibleWeek]);

  const hours = useMemo(() => {
    return Array.from({ length: HOUR_END - HOUR_START }, (_, i) => HOUR_START + i);
  }, []);

  const entriesByDay = useMemo(() => {
    const map = new Map<string, CalendarEntry[]>();
    for (const entry of entries) {
      if (!entry.start_at) continue;
      const d = new Date(entry.start_at);
      const key = `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
      const list = map.get(key) ?? [];
      list.push(entry);
      map.set(key, list);
    }
    return map;
  }, [entries]);

  const handleDragStart = (e: React.DragEvent<HTMLDivElement>, entry: CalendarEntry) => {
    e.dataTransfer.setData('application/x-calendar-entry', entry.id);
    e.dataTransfer.effectAllowed = 'move';
  };

  const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
    if (e.dataTransfer.types.includes('application/x-calendar-entry')) {
      e.preventDefault();
      e.dataTransfer.dropEffect = 'move';
    }
  };

  const handleDrop = (e: React.DragEvent<HTMLDivElement>, day: Date, hour: number) => {
    e.preventDefault();
    const id = e.dataTransfer.getData('application/x-calendar-entry');
    if (!id) return;
    const entry = entries.find((x) => x.id === id);
    if (!entry || !entry.start_at) return;
    const oldStart = new Date(entry.start_at);
    const newStart = new Date(
      day.getFullYear(),
      day.getMonth(),
      day.getDate(),
      hour,
      oldStart.getMinutes(),
      oldStart.getSeconds(),
    );
    void onMoveEntry(entry, newStart);
  };

  return (
    <div className="flex flex-col h-full bg-white" data-testid="week-view">
      {/* Header row: weekday names + date numbers */}
      <div className="flex border-b border-secondary-200 bg-secondary-50">
        <div className="w-16 flex-shrink-0" />
        {weekDays.map((day, i) => {
          const isToday = sameDay(day, today);
          return (
            <div
              key={i}
              className={clsx(
                'flex-1 text-center px-2 py-2 border-l border-secondary-200',
                isToday && 'bg-primary-50',
              )}
            >
              <div className="text-xs font-medium text-secondary-600 uppercase tracking-wide">
                {weekdays[i]}
              </div>
              <div
                className={clsx(
                  'text-sm font-semibold mt-0.5 inline-flex items-center justify-center h-6 w-6 rounded-full',
                  isToday && 'bg-primary-600 text-white',
                )}
              >
                {day.getDate()}
              </div>
            </div>
          );
        })}
      </div>

      {/* Scrollable time grid */}
      <div className="flex-1 overflow-y-auto" data-testid="week-view-grid">
        {/* Time column + day columns */}
        <div className="flex">
          {/* Time labels */}
          <div className="w-16 flex-shrink-0">
            {hours.map((hour) => (
              <div
                key={hour}
                className="text-xs text-secondary-400 text-right pr-2 border-b border-secondary-100"
                style={{ height: `${SLOT_HEIGHT}px` }}
              >
                {String(hour).padStart(2, '0')}:00
              </div>
            ))}
          </div>

          {/* Day columns */}
          {weekDays.map((day, dayIdx) => {
            const key = `${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`;
            const dayEntries = entriesByDay.get(key) ?? [];
            const isToday = sameDay(day, today);
            return (
              <div
                key={dayIdx}
                className={clsx(
                  'flex-1 relative border-l border-secondary-200',
                  isToday && 'bg-primary-50/30',
                )}
              >
                {/* Hour slots */}
                {hours.map((hour) => (
                  <div
                    key={hour}
                    className="border-b border-secondary-100 hover:bg-primary-50 cursor-pointer"
                    style={{ height: `${SLOT_HEIGHT}px` }}
                    onClick={() => !loading && onCreateAt(new Date(day.getFullYear(), day.getMonth(), day.getDate(), hour, 0))}
                    onDragOver={handleDragOver}
                    onDrop={(e) => handleDrop(e, day, hour)}
                    data-testid={`week-slot-${dayIdx}-${hour}`}
                  />
                ))}

                {/* Entries positioned absolutely */}
                {dayEntries.map((entry) => {
                  if (!entry.start_at) return null;
                  const start = new Date(entry.start_at);
                  const end = entry.end_at ? new Date(entry.end_at) : new Date(start.getTime() + 60 * 60 * 1000);
                  const startHour = start.getHours() + start.getMinutes() / 60;
                  const endHour = end.getHours() + end.getMinutes() / 60;
                  const durationHours = Math.max(endHour - startHour, 0.5);
                  const top = (startHour - HOUR_START) * SLOT_HEIGHT;
                  const height = durationHours * SLOT_HEIGHT;
                  return (
                    <div
                      key={entry.id}
                      draggable
                      onDragStart={(e) => handleDragStart(e, entry)}
                      onClick={(e) => {
                        e.stopPropagation();
                        onEditEntry(entry);
                      }}
                      className={clsx(
                        'absolute left-1 right-1 rounded px-1.5 py-1 text-xs cursor-grab active:cursor-grabbing',
                        'bg-primary-100 text-primary-900 hover:bg-primary-200 overflow-hidden',
                        entry.entry_type === 'task' && 'bg-warning-100 text-warning-900 hover:bg-warning-200',
                      )}
                      style={{ top: `${top}px`, height: `${height}px`, minHeight: '20px' }}
                      title={entry.title}
                      data-testid={`week-entry-${entry.id}`}
                    >
                      <div className="font-medium truncate">{entry.title}</div>
                      <div className="text-xs opacity-75">{fmtTime(start)}</div>
                    </div>
                  );
                })}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

export default WeekView;
