import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import {
  fetchSpaceCalendarMonth,
  fetchSpaceCalendarDay,
  createCalendarEvent,
  updateCalendarEvent,
  deleteCalendarEvent,
  fetchSpaceFileContent,
  getSpaceFileRawUrl,
  getSpaceFileOfficePreviewUrl,
  getSpaceTrustedHtmlUrl,
  type CalendarEvent,
} from '../../api';
import { localToday, localTzOffset } from '../../lib/localDate';
import {
  WEEKDAYS,
  localMonth,
  shiftMonth,
  monthGridDays,
  splitWeeks,
  fmtRange,
  fmtTimeBadge,
  layoutWeekBars,
} from '../../lib/calendar';
import { isImagePreviewable, isPdfPreviewable, isHtmlPreviewable, isOfficePreviewable, officePreviewKind } from '../../lib/utils';
import { useIsMobile } from '../../hooks/useIsMobile';
import { FilePreview } from '../files/FilePreview';
import type { OfficePreviewDescriptor } from '../files/FilePreview';
import { FileTypeIcon } from '../files/FileTypeIcon';
import { SwipeableTabs } from '../mobile/SwipeableTabs';

function fmtSize(n: number): string {
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}MB`;
  if (n >= 1_000) return `${(n / 1_000).toFixed(1)}KB`;
  return `${n}B`;
}

// ── カレンダーの表示フィルター（タスク / 変更ファイル / 予定）─────────────
type CalFilters = { tasks: boolean; files: boolean; events: boolean };
const FILTER_DEFS: ReadonlyArray<{ key: keyof CalFilters; labelKey: string; icon: string }> = [
  { key: 'tasks', labelKey: 'calendar.filter.tasks', icon: '💬' },
  { key: 'files', labelKey: 'calendar.filter.files', icon: '📄' },
  { key: 'events', labelKey: 'calendar.filter.events', icon: '📌' },
];
const FILTERS_STORAGE_KEY = 'spaceCalendarFilters';
function loadFilters(): CalFilters {
  try {
    const raw = localStorage.getItem(FILTERS_STORAGE_KEY);
    if (raw) {
      const p = JSON.parse(raw) as Partial<CalFilters>;
      return { tasks: p.tasks !== false, files: p.files !== false, events: p.events !== false };
    }
  } catch { /* ignore */ }
  return { tasks: true, files: true, events: true };
}

interface SpaceCalendarProps {
  spaceId: string;
  /** open the chat for a task created in this space (switches to the chat tab). */
  onOpenChat: (taskId: number) => void;
  /** owner/admin can add/edit/delete events; viewers see read-only. */
  canEdit: boolean;
}

export function SpaceCalendar({ spaceId, onOpenChat, canEdit }: SpaceCalendarProps) {
  const { t } = useTranslation('spaces');
  const tzOffset = localTzOffset();
  const isMobile = useIsMobile();
  const [month, setMonth] = useState(localMonth());
  const [selectedDate, setSelectedDate] = useState<string | null>(null);
  const [filters, setFilters] = useState<CalFilters>(loadFilters);

  const toggleFilter = useCallback((key: keyof CalFilters) => {
    setFilters((prev) => {
      const next = { ...prev, [key]: !prev[key] };
      try { localStorage.setItem(FILTERS_STORAGE_KEY, JSON.stringify(next)); } catch { /* ignore */ }
      return next;
    });
  }, []);

  const monthQuery = useQuery({
    queryKey: ['spaceCalendarMonth', spaceId, month, tzOffset],
    queryFn: () => fetchSpaceCalendarMonth(spaceId, month, tzOffset),
  });

  const today = localToday();
  const days = useMemo(() => monthGridDays(month), [month]);
  const weeks = useMemo(() => splitWeeks(days), [days]);
  const counts = monthQuery.data?.days ?? {};
  const events = useMemo(() => monthQuery.data?.events ?? [], [monthQuery.data]);
  const monthLabel = (() => {
    const [y, m] = month.split('-');
    return t('calendar.monthLabel', { year: y, month: Number(m) });
  })();

  const grid = (
    <div className="flex flex-col gap-2">
      {/* month nav */}
      <div className="flex items-center justify-between">
        <button
          type="button"
          data-testid="space-cal-prev"
          onClick={() => setMonth(m => shiftMonth(m, -1))}
          className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-hairline bg-canvas text-slate-500 transition-colors hover:bg-surface hover:text-slate-900"
          aria-label={t('calendar.prevMonth')}
        >
          <svg className="h-4 w-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round"><path d="M10 4l-4 4 4 4" /></svg>
        </button>
        <h2 data-testid="space-cal-title" className="text-sm font-bold text-slate-800">{monthLabel}</h2>
        <button
          type="button"
          data-testid="space-cal-next"
          onClick={() => setMonth(m => shiftMonth(m, 1))}
          className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-hairline bg-canvas text-slate-500 transition-colors hover:bg-surface hover:text-slate-900"
          aria-label={t('calendar.nextMonth')}
        >
          <svg className="h-4 w-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round"><path d="M6 4l4 4-4 4" /></svg>
        </button>
      </div>

      {/* 表示フィルター（タスク / 変更ファイル / 予定） */}
      <div data-testid="space-cal-filters" className="flex flex-wrap items-center gap-1.5">
        {FILTER_DEFS.map(f => {
          const on = filters[f.key];
          return (
            <button
              key={f.key}
              type="button"
              data-testid={`space-cal-filter-${f.key}`}
              aria-pressed={on}
              onClick={() => toggleFilter(f.key)}
              className={`inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-2xs font-medium transition-colors ${
                on
                  ? 'border-[var(--brand-primary)] bg-[var(--brand-primary)]/10 text-slate-800 dark:text-slate-100'
                  : 'border-hairline bg-canvas text-slate-400 line-through'
              }`}
            >
              <span aria-hidden>{f.icon}</span>{t(f.labelKey)}
            </button>
          );
        })}
      </div>

      {/* weekday header */}
      <div className="grid grid-cols-7 gap-1 text-center text-2xs font-semibold text-slate-400">
        {WEEKDAYS.map((w, i) => (
          <div key={w} className={i === 0 ? 'text-red-400' : i === 6 ? 'text-sky-400' : ''}>{w}</div>
        ))}
      </div>

      {/* day cells + 複数日の横棒（週ごと） */}
      <div data-testid="space-cal-grid" className="flex flex-col gap-1">
        {weeks.map((week, wi) => {
          const bars = filters.events ? layoutWeekBars(week, events) : [];
          const laneCount = bars.reduce((m, b) => Math.max(m, b.lane + 1), 0);
          return (
            <div key={wi} className="grid grid-cols-7 gap-1">
              {week.map(d => {
                const inMonth = d.slice(0, 7) === month;
                const c = counts[d];
                const isToday = d === today;
                const isSelected = d === selectedDate;
                return (
                  <button
                    key={d}
                    type="button"
                    data-testid={`space-cal-day-${d}`}
                    onClick={() => setSelectedDate(d)}
                    className={`flex min-h-[2.5rem] flex-col items-stretch gap-0.5 rounded-md border p-1 text-left transition-colors ${
                      isSelected
                        ? 'border-[var(--brand-primary)] bg-surface'
                        : 'border-hairline hover:bg-surface'
                    } ${inMonth ? '' : 'opacity-40'}`}
                  >
                    <span
                      className={`text-[11px] font-semibold ${
                        isToday
                          ? 'inline-flex h-5 w-5 items-center justify-center self-start rounded-full bg-[var(--brand-primary)] text-white'
                          : 'text-slate-600'
                      }`}
                    >
                      {Number(d.slice(8, 10))}
                    </span>
                    {filters.tasks && c?.taskCount ? (
                      <span className="self-start rounded bg-sky-100 px-1 text-[9px] font-bold text-sky-700 dark:bg-sky-500/20 dark:text-sky-300" title={t('calendar.taskCount', { count: c.taskCount })}>
                        💬{c.taskCount}
                      </span>
                    ) : null}
                  </button>
                );
              })}
              {bars.length > 0 && (
                <div
                  className="col-span-7 grid grid-cols-7 gap-x-1 gap-y-0.5 pb-0.5"
                  style={{ gridTemplateRows: `repeat(${laneCount}, 1.05rem)` }}
                >
                  {bars.map(b => (
                    <button
                      key={b.ev.id}
                      type="button"
                      data-testid={`space-cal-bar-${b.ev.id}`}
                      title={t('calendar.barTitle', { title: b.ev.title, range: fmtRange(b.ev) })}
                      onClick={() => setSelectedDate(week[b.colStart - 1]!)}
                      style={{ gridColumn: `${b.colStart} / span ${b.colSpan}`, gridRow: b.lane + 1 }}
                      className={`flex items-center overflow-hidden whitespace-nowrap bg-amber-100 px-1 text-[9px] font-semibold leading-none text-amber-800 transition-colors hover:bg-amber-200 dark:bg-amber-500/25 dark:text-amber-200 ${
                        b.continuesLeft ? 'rounded-l-none' : 'rounded-l'
                      } ${b.continuesRight ? 'rounded-r-none' : 'rounded-r'}`}
                    >
                      <span className="truncate">
                        {b.continuesLeft ? '◀ ' : b.ev.time ? `${b.ev.time} ` : ''}{b.ev.title}{b.continuesRight ? ' ▶' : ''}
                      </span>
                    </button>
                  ))}
                </div>
              )}
            </div>
          );
        })}
      </div>
      {monthQuery.isError && <p className="text-xs text-red-600">{t('calendar.fetchError')}</p>}
    </div>
  );

  const panel = selectedDate ? (
    <DayPanel
      spaceId={spaceId}
      date={selectedDate}
      tzOffset={tzOffset}
      month={month}
      canEdit={canEdit}
      filters={filters}
      onOpenChat={onOpenChat}
      onClose={() => setSelectedDate(null)}
    />
  ) : (
    <div className="flex h-full items-center justify-center p-6 text-center text-sm text-slate-500">
      {t('calendar.emptyHint')}
    </div>
  );

  // モバイル: 上に月グリッド（左右スワイプで月送り）、下に日詳細の上下 2 分割。
  if (isMobile) {
    return (
      <div data-testid="space-calendar" className="flex h-full flex-col overflow-hidden">
        <div className="min-h-0 flex-1 overflow-hidden">
          <SwipeableTabs
            tabs={[shiftMonth(month, -1), month, shiftMonth(month, 1)]}
            activeTab={month}
            onTabChange={(m) => setMonth(m)}
            renderTab={(m) => (m === month ? <div className="p-3 overflow-y-auto h-full">{grid}</div> : <div className="h-full" />)}
          />
        </div>
        <div data-testid="space-cal-mobile-detail" className="h-[45%] min-h-0 shrink-0 overflow-hidden border-t border-hairline">
          {panel}
        </div>
      </div>
    );
  }

  // デスクトップ: 左に月グリッド、右に日詳細（split）。
  return (
    <div data-testid="space-calendar" className="flex h-full min-h-0 gap-3 overflow-hidden">
      <div className="w-full md:w-[440px] md:shrink-0 overflow-y-auto p-3">{grid}</div>
      <div className="hidden md:block flex-1 min-w-0 overflow-y-auto border-l border-hairline">{panel}</div>
    </div>
  );
}

interface DayPanelPreview {
  name: string;
  content: string;
  imageSrc: string;
  markdownImageBaseUrl?: string;
  trustedHtmlUrl?: string;
  office?: OfficePreviewDescriptor;
}

function DayPanel({
  spaceId,
  date,
  tzOffset,
  month,
  canEdit,
  filters,
  onOpenChat,
  onClose,
}: {
  spaceId: string;
  date: string;
  tzOffset: number;
  month: string;
  canEdit: boolean;
  filters: CalFilters;
  onOpenChat: (taskId: number) => void;
  onClose: () => void;
}) {
  const { t } = useTranslation('spaces');
  const qc = useQueryClient();
  const dayQuery = useQuery({
    queryKey: ['spaceCalendarDay', spaceId, date, tzOffset],
    queryFn: () => fetchSpaceCalendarDay(spaceId, date, tzOffset),
  });
  const [preview, setPreview] = useState<DayPanelPreview | null>(null);
  const [editing, setEditing] = useState<CalendarEvent | null>(null);
  const [showAdd, setShowAdd] = useState(false);

  const invalidate = useCallback(() => {
    qc.invalidateQueries({ queryKey: ['spaceCalendarDay', spaceId, date, tzOffset] });
    qc.invalidateQueries({ queryKey: ['spaceCalendarMonth', spaceId, month, tzOffset] });
  }, [qc, spaceId, date, tzOffset, month]);

  const handlePreview = useCallback(async (filePath: string, name: string) => {
    try {
      if (isOfficePreviewable(name)) {
        const kind = officePreviewKind(name);
        setPreview({
          name,
          content: '',
          imageSrc: '',
          office: { kind, url: getSpaceFileOfficePreviewUrl(spaceId, filePath), downloadUrl: getSpaceFileRawUrl(spaceId, filePath) },
        });
        return;
      }
      if (isImagePreviewable(name) || isPdfPreviewable(name) || isHtmlPreviewable(name)) {
        const imageSrc = getSpaceFileRawUrl(spaceId, filePath);
        const trustedHtmlUrl = isHtmlPreviewable(name) ? getSpaceTrustedHtmlUrl(spaceId, filePath) : undefined;
        setPreview({ name, content: '', imageSrc, trustedHtmlUrl });
        return;
      }
      const content = await fetchSpaceFileContent(spaceId, filePath);
      let markdownImageBaseUrl: string | undefined;
      if (/\.(md|markdown)$/i.test(name)) {
        const dir = filePath.includes('/') ? filePath.substring(0, filePath.lastIndexOf('/') + 1) : '';
        markdownImageBaseUrl = `/api/local/spaces/${spaceId}/files/raw?path=${dir}`;
      }
      setPreview({ name, content, imageSrc: '', markdownImageBaseUrl });
    } catch {
      /* preview failure is non-fatal; leave the list intact. */
    }
  }, [spaceId]);

  const day = dayQuery.data;

  return (
    <div data-testid="space-cal-day-panel" className="flex h-full flex-col">
      <div className="flex items-center justify-between gap-2 border-b border-hairline px-3 py-2">
        <h3 className="text-sm font-bold text-slate-800">{date}</h3>
        <button
          type="button"
          data-testid="space-cal-day-close"
          onClick={onClose}
          className="inline-flex h-7 w-7 items-center justify-center rounded-md text-slate-400 hover:bg-surface hover:text-slate-700"
          aria-label={t('calendar.close')}
        >
          <svg className="h-4 w-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round"><path d="M4 4l8 8M12 4l-8 8" /></svg>
        </button>
      </div>

      <div className="flex-1 min-h-0 overflow-y-auto p-3 space-y-4">
        {/* タスク */}
        {filters.tasks && (
        <section>
          <h4 className="mb-1.5 text-2xs font-bold uppercase tracking-wider text-slate-500">{t('calendar.day.tasks')}</h4>
          {day && day.tasks.length > 0 ? (
            <ul className="space-y-1">
              {day.tasks.map(task => (
                <li key={task.id}>
                  <button
                    type="button"
                    data-testid={`space-cal-task-${task.id}`}
                    onClick={() => onOpenChat(task.id)}
                    className="flex w-full items-center gap-2 rounded-md border border-hairline bg-canvas px-2 py-1.5 text-left transition-colors hover:bg-surface"
                  >
                    <span className="min-w-0 flex-1 truncate text-xs text-slate-700">{task.title || t('calendar.taskFallback', { id: task.id })}</span>
                    <span className="shrink-0 rounded bg-surface px-1.5 py-0.5 text-[10px] font-medium text-slate-500">{task.status ?? task.state}</span>
                  </button>
                </li>
              ))}
            </ul>
          ) : (
            <p className="text-xs text-slate-400">{t('calendar.day.noTasks')}</p>
          )}
        </section>
        )}

        {/* 変更ファイル */}
        {filters.files && (
        <section>
          <h4 className="mb-1.5 text-2xs font-bold uppercase tracking-wider text-slate-500">{t('calendar.day.changedFiles')}</h4>
          {day && day.files.length > 0 ? (
            <ul className="space-y-1">
              {day.files.map(f => (
                <li key={f.path}>
                  <button
                    type="button"
                    data-testid="space-cal-file"
                    data-name={f.name}
                    onClick={() => void handlePreview(f.path, f.name)}
                    className="flex w-full items-center gap-2 rounded-md border border-hairline bg-canvas px-2 py-1.5 text-left transition-colors hover:bg-surface"
                    title={f.path}
                  >
                    <FileTypeIcon name={f.name} className="h-4 w-4 shrink-0 text-slate-400" />
                    <span className="min-w-0 flex-1 truncate text-xs text-slate-700">{f.name}</span>
                    <span className="shrink-0 text-[10px] text-slate-400">{fmtSize(f.size)}</span>
                  </button>
                </li>
              ))}
            </ul>
          ) : (
            <p className="text-xs text-slate-400">{t('calendar.day.noChangedFiles')}</p>
          )}
        </section>
        )}

        {/* 予定 */}
        {filters.events && (
        <section>
          <div className="mb-1.5 flex items-center justify-between">
            <h4 className="text-2xs font-bold uppercase tracking-wider text-slate-500">{t('calendar.day.events')}</h4>
            {canEdit && !showAdd && !editing && (
              <button
                type="button"
                data-testid="space-cal-add-event-btn"
                onClick={() => setShowAdd(true)}
                className="rounded-md border border-hairline bg-canvas px-2 py-0.5 text-2xs font-medium text-slate-600 transition-colors hover:bg-surface hover:text-slate-900"
              >
                {t('calendar.day.addEvent')}
              </button>
            )}
          </div>

          {(showAdd || editing) && (
            <EventForm
              spaceId={spaceId}
              defaultDate={date}
              event={editing}
              onCancel={() => { setShowAdd(false); setEditing(null); }}
              onSaved={() => { setShowAdd(false); setEditing(null); invalidate(); }}
            />
          )}

          {day && day.events.length > 0 ? (
            <ul className="mt-1 space-y-1">
              {day.events.map(ev => (
                <li
                  key={ev.id}
                  data-testid={`space-cal-event-${ev.id}`}
                  className="flex items-start gap-2 rounded-md border border-hairline bg-canvas px-2 py-1.5"
                >
                  <span className="shrink-0 rounded bg-amber-50 px-1.5 py-0.5 text-[10px] font-bold text-amber-700 dark:bg-amber-500/20 dark:text-amber-300">
                    {fmtTimeBadge(ev)}
                  </span>
                  <div className="min-w-0 flex-1">
                    <div className="truncate text-xs font-semibold text-slate-700">{ev.title}</div>
                    {ev.endDate && ev.endDate > ev.date && (
                      <div className="text-[10px] font-medium text-amber-700 dark:text-amber-300">🗓 {fmtRange(ev)}</div>
                    )}
                    {ev.description && <div className="truncate text-[11px] text-slate-500">{ev.description}</div>}
                    {ev.createdBy === 'agent' && <div className="text-[10px] text-slate-400">{t('calendar.day.agent')}</div>}
                  </div>
                  {canEdit && (
                    <div className="flex shrink-0 items-center gap-1">
                      <button
                        type="button"
                        data-testid={`space-cal-event-edit-${ev.id}`}
                        onClick={() => { setShowAdd(false); setEditing(ev); }}
                        className="inline-flex h-6 w-6 items-center justify-center rounded text-slate-400 hover:bg-surface hover:text-slate-700"
                        aria-label={t('calendar.day.editEvent')}
                      >
                        <svg className="h-3.5 w-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M11 2l3 3-8 8H3v-3z" /></svg>
                      </button>
                      <button
                        type="button"
                        data-testid={`space-cal-event-delete-${ev.id}`}
                        onClick={async () => {
                          if (!window.confirm(t('calendar.day.deleteEventConfirm'))) return;
                          await deleteCalendarEvent(spaceId, ev.id);
                          invalidate();
                        }}
                        className="inline-flex h-6 w-6 items-center justify-center rounded text-slate-400 hover:bg-red-50 hover:text-red-700 dark:hover:bg-red-500/15 dark:hover:text-red-300"
                        aria-label={t('common:delete')}
                      >
                        <svg className="h-3.5 w-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M3 4h10M6.5 4V2.5h3V4M5 4l.5 9h5l.5-9" /></svg>
                      </button>
                    </div>
                  )}
                </li>
              ))}
            </ul>
          ) : (
            !showAdd && <p className="text-xs text-slate-400">{t('calendar.day.noEvents')}</p>
          )}
        </section>
        )}
      </div>

      {preview && (
        <FilePreview
          name={preview.name}
          content={preview.content}
          imageSrc={preview.imageSrc}
          markdownImageBaseUrl={preview.markdownImageBaseUrl}
          trustedHtmlUrl={preview.trustedHtmlUrl}
          office={preview.office}
          onClose={() => setPreview(null)}
        />
      )}
    </div>
  );
}

function EventForm({
  spaceId,
  defaultDate,
  event,
  onCancel,
  onSaved,
}: {
  spaceId: string;
  defaultDate: string;
  event: CalendarEvent | null;
  onCancel: () => void;
  onSaved: () => void;
}) {
  const { t } = useTranslation('spaces');
  const [title, setTitle] = useState(event?.title ?? '');
  const [date, setDate] = useState(event?.date ?? defaultDate);
  const [endDate, setEndDate] = useState(event?.endDate ?? '');
  const [time, setTime] = useState(event?.time ?? '');
  const [endTime, setEndTime] = useState(event?.endTime ?? '');
  const [description, setDescription] = useState(event?.description ?? '');
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState('');

  const submit = useCallback(async () => {
    if (!title.trim()) { setError(t('calendar.form.titleRequired')); return; }
    if (endDate && endDate < date) { setError(t('calendar.form.endAfterStart')); return; }
    // 終了時刻は開始時刻があるときだけ。単日は開始以降のみ（複数日は終了日側の時刻なので順序不問）。
    const effEndTime = time && endTime ? endTime : null;
    const isMultiDay = !!(endDate && endDate > date);
    if (effEndTime && !isMultiDay && effEndTime < time) {
      setError(t('calendar.form.endTimeAfterStart')); return;
    }
    setSaving(true);
    setError('');
    try {
      const payload = {
        date,
        // 終了日が開始日より後のときだけ複数日。それ以外は単日(null)に正規化。
        endDate: endDate && endDate > date ? endDate : null,
        time: time ? time : null,
        endTime: effEndTime,
        title: title.trim(),
        description: description ? description : null,
      };
      if (event) await updateCalendarEvent(spaceId, event.id, payload);
      else await createCalendarEvent(spaceId, payload);
      onSaved();
    } catch (e) {
      setError((e as Error)?.message ?? t('calendar.form.saveFailed'));
    } finally {
      setSaving(false);
    }
  }, [title, date, endDate, time, endTime, description, event, spaceId, onSaved]);

  return (
    <div data-testid="space-cal-add-event" className="mb-2 space-y-2 rounded-md border border-hairline bg-surface p-2.5">
      <input
        type="text"
        data-testid="space-cal-event-title"
        value={title}
        onChange={e => setTitle(e.target.value)}
        placeholder={t('calendar.form.titlePlaceholder')}
        className="w-full rounded-md border border-hairline bg-canvas px-2 py-1.5 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-accent-ring"
      />
      <div className="flex items-center gap-2">
        <label className="w-10 shrink-0 text-2xs font-medium text-slate-500">{t('calendar.form.start')}</label>
        <input
          type="date"
          data-testid="space-cal-event-date"
          value={date}
          onChange={e => setDate(e.target.value)}
          className="flex-1 rounded-md border border-hairline bg-canvas px-2 py-1.5 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-accent-ring"
        />
        <input
          type="time"
          data-testid="space-cal-event-time"
          value={time}
          onChange={e => setTime(e.target.value)}
          className="w-28 rounded-md border border-hairline bg-canvas px-2 py-1.5 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-accent-ring"
        />
      </div>
      <div className="flex items-center gap-2">
        <label className="w-10 shrink-0 text-2xs font-medium text-slate-500">{t('calendar.form.end')}</label>
        <input
          type="date"
          data-testid="space-cal-event-end-date"
          value={endDate}
          min={date}
          onChange={e => setEndDate(e.target.value)}
          className="flex-1 rounded-md border border-hairline bg-canvas px-2 py-1.5 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-accent-ring"
        />
        <input
          type="time"
          data-testid="space-cal-event-end-time"
          value={endTime}
          disabled={!time}
          title={!time ? t('calendar.form.endTimeHint') : undefined}
          onChange={e => setEndTime(e.target.value)}
          className="w-28 rounded-md border border-hairline bg-canvas px-2 py-1.5 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-accent-ring disabled:cursor-not-allowed disabled:opacity-50"
        />
      </div>
      {endDate && (
        <button
          type="button"
          onClick={() => setEndDate('')}
          className="self-start rounded-md border border-hairline bg-canvas px-2 py-1 text-2xs text-slate-500 hover:bg-surface"
        >
          {t('calendar.form.backToSingleDay')}
        </button>
      )}
      <textarea
        data-testid="space-cal-event-description"
        value={description}
        onChange={e => setDescription(e.target.value)}
        placeholder={t('calendar.form.notePlaceholder')}
        rows={2}
        className="w-full resize-none rounded-md border border-hairline bg-canvas px-2 py-1.5 text-xs text-slate-700 focus:outline-none focus:ring-2 focus:ring-accent-ring"
      />
      {error && <p className="text-2xs text-red-600">{error}</p>}
      <div className="flex justify-end gap-1.5">
        <button
          type="button"
          onClick={onCancel}
          disabled={saving}
          className="rounded-md border border-hairline bg-canvas px-2.5 py-1 text-2xs font-medium text-slate-600 transition-colors hover:bg-surface disabled:opacity-50"
        >
          {t('common:cancel')}
        </button>
        <button
          type="button"
          data-testid="space-cal-event-save"
          onClick={() => void submit()}
          disabled={saving}
          className="rounded-md bg-accent px-2.5 py-1 text-2xs font-bold text-accent-fg transition-colors hover:opacity-90 disabled:opacity-50"
        >
          {event ? t('calendar.form.update') : t('calendar.form.add')}
        </button>
      </div>
    </div>
  );
}
