/**
 * MonthView — monthly calendar grid with click-to-create and drag-to-move.
 *
 * Click an empty cell → fires `onCreateAt(date)`.
 * Click an entry → fires `onEditEntry(entry)`.
 * Drag an entry onto another day → calls `onMoveEntry(entry, newDate)`.
 */

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

export interface MonthViewProps {
  /** First day of the visible month (UTC). */
  visibleMonth: Date;
  /** Entries to render. Entries with start_at fall on that day. */
  entries: CalendarEntry[];
  /** Loading state — disables interactions. */
  loading?: boolean;
  /** Clicked an empty cell — create a new entry. */
  onCreateAt: (date: Date) => void;
  /** Clicked an existing entry — open editor. */
  onEditEntry: (entry: CalendarEntry) => void;
  /** Dragged an entry to a new day. */
  onMoveEntry: (entry: CalendarEntry, newStart: Date) => void | Promise<void>;
}

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 MonthView({
  visibleMonth,
  entries,
  loading,
  onCreateAt,
  onEditEntry,
  onMoveEntry,
}: MonthViewProps) {
  const { t } = useTranslation();

  const today = startOfDay(new Date());

  // Build a 6-row × 7-day grid (always 42 cells to keep height stable)
  const gridDays = useMemo(() => {
    const first = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1);
    // Week starts on Monday (DE locale)
    const dayOfWeek = (first.getDay() + 6) % 7; // 0 = Monday
    const start = addDays(first, -dayOfWeek);
    return Array.from({ length: 42 }, (_, i) => addDays(start, i));
  }, [visibleMonth]);

  // Group entries by yyyy-mm-dd
  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 weekdays = t('calendar.weekdays', { returnObjects: true }) as string[];
  const months = t('calendar.months', { returnObjects: true }) as string[];
  const headerLabel = `${months[visibleMonth.getMonth()]} ${visibleMonth.getFullYear()}`;

  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
  ) => {
    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;
    // Preserve original time-of-day, change only date
    const oldStart = new Date(entry.start_at);
    const newStart = new Date(
      day.getFullYear(),
      day.getMonth(),
      day.getDate(),
      oldStart.getHours(),
      oldStart.getMinutes(),
      oldStart.getSeconds()
    );
    void onMoveEntry(entry, newStart);
  };

  return (
    <div className="bg-white rounded-lg shadow border border-secondary-200" data-testid="month-view">
      <div
        className="px-4 py-3 border-b border-secondary-200 text-base font-semibold text-secondary-900"
        data-testid="month-view-header"
      >
        {headerLabel}
      </div>

      <div className="grid grid-cols-7 border-b border-secondary-200 bg-secondary-50">
        {weekdays.map((wd) => (
          <div
            key={wd}
            className="px-2 py-2 text-xs font-medium text-secondary-600 text-center uppercase tracking-wide"
          >
            {wd}
          </div>
        ))}
      </div>

      <div className="grid grid-cols-7 grid-rows-6" data-testid="month-view-grid">
        {gridDays.map((day) => {
          const inMonth = day.getMonth() === visibleMonth.getMonth();
          const isToday = sameDay(day, today);
          const key = `${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`;
          const dayEntries = entriesByDay.get(key) ?? [];
          return (
            <div
              key={key}
              role="button"
              tabIndex={0}
              data-testid={`month-cell-${key}`}
              onClick={() => !loading && onCreateAt(day)}
              onKeyDown={(e) => {
                if ((e.key === 'Enter' || e.key === ' ') && !loading) {
                  e.preventDefault();
                  onCreateAt(day);
                }
              }}
              onDragOver={handleDragOver}
              onDrop={(e) => handleDrop(e, day)}
              className={clsx(
                'border border-secondary-100 min-h-[6rem] p-1 align-top cursor-pointer',
                'hover:bg-primary-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
                !inMonth && 'bg-secondary-50 text-secondary-400',
                loading && 'opacity-60 pointer-events-none'
              )}
            >
              <div
                className={clsx(
                  'text-xs font-medium mb-1 inline-flex items-center justify-center',
                  'h-6 w-6 rounded-full',
                  isToday && 'bg-primary-600 text-white'
                )}
                data-testid={`month-day-number-${key}`}
              >
                {day.getDate()}
              </div>
              <div className="space-y-1">
                {dayEntries.slice(0, 3).map((entry) => {
                  const time = entry.start_at ? fmtTime(new Date(entry.start_at)) : '';
                  return (
                    <div
                      key={entry.id}
                      draggable
                      onDragStart={(e) => handleDragStart(e, entry)}
                      onClick={(e) => {
                        e.stopPropagation();
                        onEditEntry(entry);
                      }}
                      data-testid={`month-entry-${entry.id}`}
                      className={clsx(
                        'text-xs truncate rounded px-1 py-0.5 cursor-grab active:cursor-grabbing',
                        'bg-primary-100 text-primary-900 hover:bg-primary-200'
                      )}
                      title={entry.title}
                    >
                      {time && <span className="font-mono mr-1">{time}</span>}
                      {entry.title}
                    </div>
                  );
                })}
                {dayEntries.length > 3 && (
                  <div className="text-xs text-secondary-500 px-1">
                    +{dayEntries.length - 3} {t('calendar.noEntries').includes('Keine')
                      ? 'weitere'
                      : 'more'}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default MonthView;
