import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery, useQueries } from '@tanstack/react-query';
import {
  fetchCrossSpaceCalendarMonth,
  fetchSpaceCalendarDay,
  type CrossCalendarSpace,
  type CalendarEvent,
} from '../../api';
import { localToday, localTzOffset } from '../../lib/localDate';
import {
  WEEKDAYS,
  localMonth,
  shiftMonth,
  monthGridDays,
  splitWeeks,
  fmtRange,
  fmtTimeBadge,
  layoutWeekBars,
} from '../../lib/calendar';
import { useIsMobile } from '../../hooks/useIsMobile';
import { SwipeableTabs } from '../mobile/SwipeableTabs';

interface CrossSpaceCalendarProps {
  /** open a space's detail (switches to the Spaces page). */
  onOpenSpace: (spaceId: string) => void;
  /** open a task thread (switches to the Tasks page). */
  onOpenTask: (taskId: number) => void;
}

export function CrossSpaceCalendar({ onOpenSpace, onOpenTask }: CrossSpaceCalendarProps) {
  const { t } = useTranslation('spaces');
  const tzOffset = localTzOffset();
  const isMobile = useIsMobile();
  const [month, setMonth] = useState(localMonth());
  const [selectedDate, setSelectedDate] = useState<string | null>(null);

  const monthQuery = useQuery({
    queryKey: ['crossCalendarMonth', month, tzOffset],
    queryFn: () => fetchCrossSpaceCalendarMonth(month, tzOffset),
  });

  const today = localToday();
  const days = useMemo(() => monthGridDays(month), [month]);
  const weeks = useMemo(() => splitWeeks(days), [days]);
  const counts = monthQuery.data?.days ?? {};
  const spaces = monthQuery.data?.spaces ?? [];
  const events = useMemo(() => monthQuery.data?.events ?? [], [monthQuery.data]);
  const spaceById = useMemo(() => {
    const m = new Map<string, CrossCalendarSpace>();
    for (const s of spaces) m.set(s.id, s);
    return m;
  }, [spaces]);

  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="cross-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="cross-cal-title" className="text-sm font-bold text-slate-800">{monthLabel}</h2>
        <button
          type="button"
          data-testid="cross-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>

      {/* 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="cross-cal-grid" className="flex flex-col gap-1">
        {weeks.map((week, wi) => {
          const bars = 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 bySpace = counts[d];
                // ドットは「タスクのある」スペースだけ。予定は下の横棒で表す。
                const taskSpaceIds = bySpace
                  ? Object.keys(bySpace).filter(sid => bySpace[sid]!.taskCount > 0)
                  : [];
                const isToday = d === today;
                const isSelected = d === selectedDate;
                return (
                  <button
                    key={d}
                    type="button"
                    data-testid={`cross-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>
                    {taskSpaceIds.length > 0 && (
                      <span className="flex flex-wrap gap-0.5">
                        {taskSpaceIds.slice(0, 6).map(sid => {
                          const sp = spaceById.get(sid);
                          return (
                            <span
                              key={sid}
                              data-testid={`cross-cal-dot-${sid}`}
                              className="inline-block h-2 w-2 rounded-full"
                              style={{ backgroundColor: sp?.color ?? 'var(--brand-primary)' }}
                              title={t('crossCalendar.dotTitle', { name: sp?.name ?? sid, count: bySpace?.[sid]?.taskCount ?? 0 })}
                            />
                          );
                        })}
                        {taskSpaceIds.length > 6 && (
                          <span className="text-[8px] font-bold text-slate-400">+{taskSpaceIds.length - 6}</span>
                        )}
                      </span>
                    )}
                  </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 => {
                    const sp = spaceById.get(b.ev.spaceId);
                    const color = sp?.color ?? 'var(--brand-primary)';
                    return (
                      <button
                        key={b.ev.id}
                        type="button"
                        data-testid={`cross-cal-bar-${b.ev.id}`}
                        title={`${sp?.name ? t('crossCalendar.barTitlePrefix', { name: sp.name }) : ''}${t('crossCalendar.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,
                          backgroundColor: `color-mix(in srgb, ${color} 22%, transparent)`,
                          borderLeft: `2px solid ${color}`,
                        }}
                        className={`flex items-center overflow-hidden whitespace-nowrap px-1 text-[9px] font-semibold leading-none text-slate-700 transition-opacity hover:opacity-80 dark:text-slate-100 ${
                          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 ? (
    <CrossDayPanel
      date={selectedDate}
      tzOffset={tzOffset}
      counts={counts[selectedDate] ?? {}}
      events={events}
      spaces={spaces}
      onOpenSpace={onOpenSpace}
      onOpenTask={onOpenTask}
      onClose={() => setSelectedDate(null)}
    />
  ) : (
    <div className="flex h-full items-center justify-center p-6 text-center text-sm text-slate-500">
      {t('crossCalendar.emptyHint')}
    </div>
  );

  // モバイル: 上に月グリッド（左右スワイプで月送り）、下に日詳細の上下 2 分割。
  if (isMobile) {
    return (
      <div data-testid="cross-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="cross-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="cross-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>
  );
}

function CrossDayPanel({
  date,
  tzOffset,
  counts,
  events,
  spaces,
  onOpenSpace,
  onOpenTask,
  onClose,
}: {
  date: string;
  tzOffset: number;
  /** {[spaceId]: {taskCount,eventCount}} for this day (from the month aggregate). */
  counts: Record<string, { taskCount: number; eventCount: number }>;
  /** その月の全イベント（spaceId 付き）。月集計のクリップ外の日でもバーから補完する。 */
  events: CalendarEvent[];
  spaces: CrossCalendarSpace[];
  onOpenSpace: (spaceId: string) => void;
  onOpenTask: (taskId: number) => void;
  onClose: () => void;
}) {
  const { t: tr } = useTranslation('spaces');
  // 月集計 counts は当月内にクリップされるため、月境界をまたぐ予定の隣月側の日を
  // 選ぶと活動なし扱いになってしまう。選択日に重なる予定の spaceId を直接補完する。
  const eventSpaceIds = useMemo(() => {
    const ids = new Set<string>();
    for (const ev of events) {
      const end = ev.endDate && ev.endDate > ev.date ? ev.endDate : ev.date;
      if (ev.date <= date && date <= end) ids.add(ev.spaceId);
    }
    return ids;
  }, [events, date]);

  // Only fetch the per-space day detail for spaces that have activity on this
  // day — avoids a second cross endpoint and avoids N empty fetches.
  const activeSpaces = useMemo(
    () => spaces.filter(s => {
      const c = counts[s.id];
      return (c && (c.taskCount + c.eventCount) > 0) || eventSpaceIds.has(s.id);
    }),
    [spaces, counts, eventSpaceIds],
  );

  const dayQueries = useQueries({
    queries: activeSpaces.map(s => ({
      queryKey: ['crossCalendarDay', s.id, date, tzOffset],
      queryFn: () => fetchSpaceCalendarDay(s.id, date, tzOffset),
    })),
  });

  return (
    <div data-testid="cross-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="cross-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={tr('crossCalendar.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">
        {activeSpaces.length === 0 && (
          <p className="text-xs text-slate-400">{tr('crossCalendar.noActiveSpaces')}</p>
        )}
        {activeSpaces.map((sp, i) => {
          const day = dayQueries[i]?.data;
          return (
            <section key={sp.id} data-testid={`cross-cal-space-${sp.id}`}>
              <button
                type="button"
                onClick={() => onOpenSpace(sp.id)}
                className="mb-1.5 flex w-full items-center gap-2 text-left"
              >
                <span className="inline-block h-2.5 w-2.5 shrink-0 rounded-full" style={{ backgroundColor: sp.color }} />
                <span className="min-w-0 flex-1 truncate text-xs font-bold text-slate-700 hover:text-slate-900">{sp.name}</span>
              </button>

              {/* タスク */}
              {day && day.tasks.length > 0 && (
                <ul className="space-y-1">
                  {day.tasks.map(t => (
                    <li key={t.id}>
                      <button
                        type="button"
                        data-testid={`cross-cal-task-${t.id}`}
                        onClick={() => onOpenTask(t.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">{t.title || tr('crossCalendar.taskFallback', { id: t.id })}</span>
                        <span className="shrink-0 rounded bg-surface px-1.5 py-0.5 text-[10px] font-medium text-slate-500">{t.status ?? t.state}</span>
                      </button>
                    </li>
                  ))}
                </ul>
              )}

              {/* 予定 */}
              {day && day.events.length > 0 && (
                <ul className="mt-1 space-y-1">
                  {day.events.map((ev: CalendarEvent) => (
                    <li
                      key={ev.id}
                      data-testid={`cross-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>}
                      </div>
                    </li>
                  ))}
                </ul>
              )}

              {day && day.tasks.length === 0 && day.events.length === 0 && (
                <p className="text-xs text-slate-400">{tr('crossCalendar.noItems')}</p>
              )}
            </section>
          );
        })}
      </div>
    </div>
  );
}
