import { useMemo, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { ApiRequestError } from '../../api/client';
import { exhibitionApi } from '../../api/endpoints';
import type { ExhibitionDto } from '../../api/types';
import { StatusBadge } from '../../components/ui/Badge';
import { Button } from '../../components/ui/Button';
import { EmptyState, ErrorState, Skeleton } from '../../components/ui/States';
import {
  IconCalendar,
  IconGrid,
  IconUsers,
} from '../../components/ui/icons';
import {
  ALL_EVENTS,
  CATEGORY_LABEL,
  EVENT_YEARS,
  STATUS_LABEL,
  eventDaysFor,
  hallUtilizationFor,
  type EventCategory,
  type EventStatus,
  type HallUtilRow,
  type ScheduleEvent,
} from './sampleSchedule';
import KxCalendarView, { type KxCalendarItem } from './KxCalendarView';
import { fetchHolidaySet } from '../admin/holidayApi';
import { scheduleApi } from './scheduleApi';
import { hallAssignApi } from '../hallassign/hallAssignApi';
import './schedule.css';

/** 카테고리별 막대/칩 강조색(schedule.css chip 색과 정렬). */
const CAT_COLOR: Record<string, string> = {
  exhibition: '#0066b3',
  culture: '#b83268',
  trade: '#9a6b1f',
  tech: '#1f7a54',
};

/** ScheduleEvent → KxCalendarItem(기간 막대·상태 색점). */
function toCalItem(e: ScheduleEvent): KxCalendarItem {
  return {
    id: String(e.id),
    startDate: e.start,
    endDate: e.end,
    title: e.title,
    color: CAT_COLOR[e.category] ?? '#0066b3',
    statusCd: e.status === 'ongoing' ? 'ONGOING' : e.status === 'upcoming' ? 'UPCOMING' : 'DONE',
  };
}

/** 집계 API 실패 시 크롤링 샘플로 강등할지 판정(NETWORK/NOT_FOUND/NOT_IMPLEMENTED). */
function isDegradable(err: unknown): boolean {
  return (
    err instanceof ApiRequestError &&
    (err.code === 'NETWORK' || err.code === 'NOT_FOUND' || err.code === 'NOT_IMPLEMENTED')
  );
}

const MOCK = `${import.meta.env.BASE_URL}mock/organizer_dashboard`;
const THUMBS = [`${MOCK}/img1.jpg`, `${MOCK}/img2.jpg`, `${MOCK}/img3.jpg`];
const TODAY = new Date(2026, 6, 11); // 2026-07-11 (currentDate)
const TODAY_YMD = '2026-07-11';

const KNOWN_CATEGORIES = new Set<EventCategory>(['exhibition', 'culture', 'trade', 'tech']);

/** 날짜 기준 상태 재계산 — DB status 원문 대신 TODAY 기준 진행/예정/종료로 정규화. */
function statusFromDates(start: string, end: string): EventStatus {
  if (end < TODAY_YMD) return 'ended';
  if (start > TODAY_YMD) return 'upcoming';
  return 'ongoing';
}

/** "2026-08-11","2026-08-14" → "2026.08.11–08.14" */
function periodLabel(start: string, end: string): string {
  const s = start.split('-').join('.');
  const e = end.split('-').join('.');
  return e.startsWith(s.slice(0, 5)) ? `${s}–${e.slice(5)}` : `${s}–${e}`;
}

/** 실 API ExhibitionDto → 화면 ScheduleEvent 매핑. DB 미포함 필드(카테고리·홀·인원)는 기본값. */
function toScheduleEvent(e: ExhibitionDto, i: number): ScheduleEvent {
  const known = KNOWN_CATEGORIES.has(e.category as EventCategory);
  const category = (known ? (e.category as EventCategory) : 'exhibition') as EventCategory;
  return {
    id: e.id,
    title: e.name,
    category,
    categoryDefaulted: !known,
    status: statusFromDates(e.startDate, e.endDate),
    period: periodLabel(e.startDate, e.endDate),
    start: e.startDate,
    end: e.endDate,
    halls: e.hallLabel ?? '홀 미배정',
    estVisitors: e.estVisitors != null ? `${e.estVisitors.toLocaleString()}명` : '집계 대기',
    thumbIdx: i % THUMBS.length,
  };
}

/** 홀 필터 매칭 — halls 문자열("홀 4, 5A"·"제2전시장 홀7")에서 홀 번호만 추출해 전시장 범위("1-5"/"6-10")와 대조. "제N전시장"의 N·"7A-1"의 -1 오매칭 방지. */
function matchesHall(halls: string, range: string): boolean {
  if (range === 'all') return true;
  const [lo, hi] = range.split('-').map(Number);
  const cleaned = halls
    .replace(/제\s*\d+\s*전시장/g, '')
    .replace(/(\d+[A-C]?)-\d+/g, '$1');
  const nums = (cleaned.match(/\d+/g) ?? []).map(Number).filter((n) => n >= 1 && n <= 10);
  return nums.some((n) => n >= lo && n <= hi);
}

/*
 * SCR-15 전시 일정 관리 (M1·M6). Stitch exhibition_schedule 이식.
 * - 좌 2/3 행사 리스트(또는 WISE 스타일 월 캘린더) + 우 1/3 미니 캘린더 · 홀 가동률.
 * - 폴백 데이터: kintex.com 크롤링 10년치(2016~2026, 1,098건) — sampleSchedule.ALL_EVENTS.
 * - 캘린더 뷰는 WISE(UIWS) ScheduleCalendar 월 그리드 패턴 참조(외부 라이브러리 미사용,
 *   이전/다음/오늘 툴바·요일 헤더·당일 강조·일자별 이벤트 칩).
 * ★ 경계면: 행사 목록은 인증 workspaces(실 원천) 우선, 없으면 크롤링 데이터 폴백.
 */
export function ExhibitionSchedulePage() {
  const { t } = useTranslation();
  const [view, setView] = useState<'list' | 'calendar'>('list');
  const [year, setYear] = useState('2026');
  const [hall, setHall] = useState('all');
  const [category, setCategory] = useState('all');
  const [status, setStatus] = useState('all');
  // 우측 미니 캘린더·홀 가동률 공유 월 커서
  const [asideCursor, setAsideCursor] = useState(new Date(2026, 6, 1)); // 2026년 7월

  /*
   * 실 원천: `GET /api/events` 전 홀 10년치 카탈로그(ExhibitionDto[]) — event 테이블(V10 시드).
   * 백엔드 미구현(NOT_IMPLEMENTED/NOT_FOUND)·네트워크 오류 시에만 크롤링 샘플(ALL_EVENTS)로 폴백한다.
   * ALL_EVENTS는 백엔드 V10 seed_exhibitions_10yr 와 동일 원천(kintex.com 크롤링)이라 shape·건수 정합.
   */
  const query = useQuery<{ events: ScheduleEvent[]; degraded: boolean }>({
    queryKey: ['exhibitions'],
    queryFn: async () => {
      try {
        const rows = await exhibitionApi.list();
        return { events: rows.map(toScheduleEvent), degraded: false };
      } catch (err) {
        if (
          err instanceof ApiRequestError &&
          (err.code === 'NOT_IMPLEMENTED' || err.code === 'NOT_FOUND' || err.code === 'NETWORK')
        ) {
          return { events: ALL_EVENTS, degraded: true };
        }
        throw err;
      }
    },
  });

  const events = query.data?.events ?? [];
  const degraded = query.data?.degraded ?? false;

  const filtered = useMemo(
    () =>
      events.filter(
        (e) =>
          (year === 'all' || e.start.slice(0, 4) === year || e.end.slice(0, 4) === year) &&
          matchesHall(e.halls, hall) &&
          (category === 'all' || e.category === category) &&
          (status === 'all' || e.status === status),
      ),
    [events, year, hall, category, status],
  );

  // 목록은 최신 시작일 우선 정렬
  const listed = useMemo(() => [...filtered].sort((a, b) => (a.start < b.start ? 1 : -1)), [filtered]);

  // 캘린더(월/주/일) 항목 — 필터된 행사(기간=start~end) 매핑.
  const calItems = useMemo(() => filtered.map(toCalItem), [filtered]);

  // 공휴일 세트(캘린더 붉은색 + 공휴일명 표시) — 올해±1년 창 조회. 실패해도 캘린더 정상(빈 세트).
  const holidayQ = useQuery({
    queryKey: ['exh-holidays'],
    queryFn: () =>
      fetchHolidaySet(
        `${Number(TODAY_YMD.slice(0, 4)) - 1}-01-01`,
        `${Number(TODAY_YMD.slice(0, 4)) + 1}-12-31`,
      ),
    retry: false,
  });
  const holidays = holidayQ.data ?? new Map<string, string>();
  const catLegend = useMemo(
    () =>
      Object.keys(CATEGORY_LABEL).map((k) => ({
        code: k,
        label: t(`schedule.cat.${k}`),
        color: CAT_COLOR[k],
      })),
    [t],
  );

  return (
    <div className="kx-sched">
      <header className="kx-sched__head">
        <div>
          <h1 className="kx-sched__title">{t('schedule.title')}</h1>
          <p className="kx-sched__subtitle">
            {t('schedule.subtitle', { n: events.length.toLocaleString() })}
            {degraded && ` ${t('schedule.crawlFallback')}`}
          </p>
        </div>
      </header>

      <div className="kx-sched__filters">
        <select className="kx-select" aria-label={t('schedule.yearFilter')} value={year} onChange={(e) => setYear(e.target.value)}>
          <option value="all">{t('schedule.allYears')}</option>
          {EVENT_YEARS.map((y) => (
            <option key={y} value={String(y)}>{t('schedule.yearN', { y })}</option>
          ))}
        </select>
        <select className="kx-select" aria-label={t('schedule.hallFilter')} value={hall} onChange={(e) => setHall(e.target.value)}>
          <option value="all">{t('schedule.allHalls')}</option>
          <option value="1-5">{t('schedule.hall1')}</option>
          <option value="6-10">{t('schedule.hall2')}</option>
        </select>
        <select className="kx-select" aria-label={t('schedule.catFilter')} value={category} onChange={(e) => setCategory(e.target.value)}>
          <option value="all">{t('schedule.allCats')}</option>
          {Object.keys(CATEGORY_LABEL).map((k) => (
            <option key={k} value={k}>{t(`schedule.cat.${k}`)}</option>
          ))}
        </select>
        <select className="kx-select" aria-label={t('schedule.statusFilter')} value={status} onChange={(e) => setStatus(e.target.value)}>
          <option value="all">{t('schedule.allStatus')}</option>
          {Object.keys(STATUS_LABEL).map((k) => (
            <option key={k} value={k}>{t(`schedule.status.${k}`)}</option>
          ))}
        </select>
        <div className="kx-sched__filter-spacer" />
        {degraded && <span className="kx-bi__degraded">{t('schedule.apiWait')}</span>}
        <div className="kx-seg" role="tablist" aria-label={t('schedule.viewSwitch')}>
          <button role="tab" aria-selected={view === 'list'} className={`kx-seg__btn ${view === 'list' ? 'is-active' : ''}`} onClick={() => setView('list')}>{t('schedule.viewList')}</button>
          <button role="tab" aria-selected={view === 'calendar'} className={`kx-seg__btn ${view === 'calendar' ? 'is-active' : ''}`} onClick={() => setView('calendar')}>{t('schedule.viewCalendar')}</button>
        </div>
      </div>

      <div className="kx-sched__split">
        {query.isLoading ? (
          <section className="kx-sched__list" aria-label={t('schedule.listLoading')} aria-busy="true">
            {Array.from({ length: 5 }).map((_, i) => (
              <article key={i} className="kx-evcard" aria-hidden="true">
                <Skeleton height={112} width={160} radius={8} />
                <div className="kx-evcard__body" style={{ flex: 1 }}>
                  <Skeleton height={20} width="60%" />
                  <Skeleton height={14} width="35%" />
                  <Skeleton height={14} width="80%" />
                  <Skeleton height={14} width="50%" />
                </div>
              </article>
            ))}
          </section>
        ) : query.isError ? (
          <section className="kx-sched__list" aria-label={t('schedule.listAria')}>
            <ErrorState
              message={t('schedule.loadError')}
              onRetry={() => query.refetch()}
            />
          </section>
        ) : view === 'list' ? (
          <section className="kx-sched__list" aria-label={t('schedule.listAria')}>
            <p className="kx-sched__count tnum">{t('schedule.count', { n: listed.length.toLocaleString() })}</p>
            {listed.length === 0 ? (
              <div className="kx-card">
                <EmptyState
                  title={t('schedule.emptyTitle')}
                  description={t('schedule.emptyDesc')}
                  action={<Button>{t('schedule.newEvent')}</Button>}
                />
              </div>
            ) : (
              listed.map((e) => (
                <article key={e.id} className="kx-evcard">
                  <div className="kx-evcard__poster" style={{ backgroundImage: `url(${THUMBS[e.thumbIdx]})` }} aria-hidden="true" />
                  <div className="kx-evcard__body">
                    <div className="kx-evcard__top">
                      <h2 className="kx-evcard__title">{e.title}</h2>
                      <span className={`kx-evstatus is-${e.status}`}>{t(`schedule.status.${e.status}`)}</span>
                    </div>
                    <p className="kx-evcard__cat">
                      {t('schedule.catExhibit', { cat: t(`schedule.cat.${e.category}`) })}
                      {e.categoryDefaulted && (
                        <span
                          className="kx-evcard__cat-default"
                          title={t('schedule.catDefaultedTip', { defaultValue: '행사 분류 미지정 — 기본값(전시)으로 표시' })}
                        >
                          {t('schedule.catDefaulted', { defaultValue: '기본값' })}
                        </span>
                      )}
                    </p>
                    <ul className="kx-evcard__meta">
                      <li aria-label={t('schedule.period')}><IconCalendar size={14} /> <span className="tnum">{e.period}</span></li>
                      <li aria-label={t('schedule.assignedHall')}><IconGrid size={14} /> {e.halls}</li>
                      <li aria-label={t('schedule.estVisitors')}><IconUsers size={14} /> <span className="tnum">{e.estVisitors}</span></li>
                      {e.designStatus && <li><StatusBadge status={e.designStatus} /></li>}
                    </ul>
                    <div className="kx-evcard__actions">
                      <Button variant="secondary">{t('schedule.editDetail')}</Button>
                      <Button variant="ghost">{t('schedule.viewLogistics')}</Button>
                    </div>
                  </div>
                </article>
              ))
            )}
          </section>
        ) : (
          <KxCalendarView
            items={calItems}
            legend={catLegend}
            today={TODAY_YMD}
            initialDate={TODAY_YMD}
            holidays={holidays}
          />
        )}

        <aside className="kx-sched__aside">
          <MiniCalendar cursor={asideCursor} onCursor={setAsideCursor} holidays={holidays} />
          <HallAvailability cursor={asideCursor} />
          <HallUtilization cursor={asideCursor} />
        </aside>
      </div>
    </div>
  );
}

const WD = ['일', '월', '화', '수', '목', '금', '토'];

function MiniCalendar({
  cursor,
  onCursor,
  holidays,
}: {
  cursor: Date;
  onCursor: (d: Date) => void;
  holidays?: Map<string, string>;
}) {
  const { t } = useTranslation();
  const year = cursor.getFullYear();
  const month = cursor.getMonth();
  const firstDay = new Date(year, month, 1).getDay();
  const daysInMonth = new Date(year, month + 1, 0).getDate();

  /*
   * 실 원천: `GET /api/events/calendar?year=&month=` — event 테이블 기반 월 점유 집계.
   * 실패(NETWORK/NOT_FOUND/NOT_IMPLEMENTED) 시에만 크롤링 샘플(eventDaysFor)로 폴백.
   * eventDays 키 포맷은 `${year}-${month+1}-${day}`(무패딩) — 셀 렌더와 정합.
   */
  const query = useQuery<{ days: Set<string>; degraded: boolean }>({
    queryKey: ['schedule-calendar', year, month],
    queryFn: async () => {
      try {
        const rows = await scheduleApi.calendar(year, month + 1);
        const days = new Set<string>();
        for (const r of rows) days.add(`${year}-${month + 1}-${Number(r.date.slice(8, 10))}`);
        return { days, degraded: false };
      } catch (err) {
        if (isDegradable(err)) return { days: eventDaysFor(year, month), degraded: true };
        throw err;
      }
    },
  });
  const eventDays = query.data?.days ?? new Set<string>();
  const degraded = query.data?.degraded ?? false;

  const cells: (number | null)[] = [];
  for (let i = 0; i < firstDay; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);

  const isToday = (d: number) =>
    TODAY.getFullYear() === year && TODAY.getMonth() === month && TODAY.getDate() === d;

  return (
    <section className="kx-card kx-cal" aria-label={t('schedule.miniCal')}>
      <div className="kx-cal__head">
        <button className="kx-cal__nav" aria-label={t('schedule.prevMonth')} onClick={() => onCursor(new Date(year, month - 1, 1))}>‹</button>
        <strong>{t('schedule.yearMonth', { y: year, m: month + 1 })}{degraded && <span className="kx-bi__degraded"> {t('schedule.offlineSample')}</span>}</strong>
        <button className="kx-cal__nav" aria-label={t('schedule.nextMonth')} onClick={() => onCursor(new Date(year, month + 1, 1))}>›</button>
      </div>
      <div className="kx-cal__grid" role="grid">
        {WD.map((w, i) => (
          <span key={w} className="kx-cal__wd" role="columnheader">{t(`common.weekday.${i}`)}</span>
        ))}
        {cells.map((d, i) => {
          if (d === null) return <span key={`e${i}`} className="kx-cal__cell is-empty" />;
          const hasEvent = eventDays.has(`${year}-${month + 1}-${d}`);
          const hkey = `${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
          const holidayName = holidays?.get(hkey);
          return (
            <span
              key={d}
              className={`kx-cal__cell tnum ${isToday(d) ? 'is-today' : ''} ${hasEvent ? 'has-event' : ''} ${holidayName ? 'is-holiday' : ''}`}
              role="gridcell"
              title={holidayName || undefined}
              aria-label={
                holidayName
                  ? `${t('schedule.day', { d })} ${holidayName}`
                  : hasEvent
                    ? t('schedule.dayHasEvent', { d })
                    : t('schedule.day', { d })
              }
            >
              {d}
            </span>
          );
        })}
      </div>
    </section>
  );
}

/**
 * F001 가용성 보강 — 홀 가용(빈 기간) 표시. hallassign API(홀 마스터) + 기존 홀 가동률 집계를 재사용해
 * 지정 월의 홀 그룹별 "가용 일수(총일-점유일)"와 가용 여부 배지를 보여준다. 집계 실패 시 홀 마스터만으로
 * "가용 미확인"으로 강등한다(외부 라이브러리·신규 백엔드 없음).
 */
function HallAvailability({ cursor }: { cursor: Date }) {
  const { t } = useTranslation();
  const year = cursor.getFullYear();
  const month = cursor.getMonth();

  const query = useQuery<{
    rows: Array<{ label: string; freeDays: number; totalDays: number; available: boolean }>;
    hallCount: number;
    degraded: boolean;
  }>({
    queryKey: ['schedule-hall-avail', year, month],
    queryFn: async () => {
      const halls = await hallAssignApi.halls().catch(() => [] as Awaited<ReturnType<typeof hallAssignApi.halls>>);
      try {
        const util = await scheduleApi.hallUtilization(year, month + 1);
        const rows = util.map((h) => {
          const totalDays = h.totalDays || 0;
          const freeDays = Math.max(0, totalDays - (h.busyDays || 0));
          return { label: h.label, freeDays, totalDays, available: totalDays === 0 || freeDays > 0 };
        });
        return { rows, hallCount: halls.length, degraded: false };
      } catch (err) {
        if (isDegradable(err)) {
          const rows = hallUtilizationFor(year, month).map((h) => ({
            label: h.label,
            freeDays: 0,
            totalDays: 0,
            available: h.pct < 100,
          }));
          return { rows, hallCount: halls.length, degraded: true };
        }
        throw err;
      }
    },
  });

  const data = query.data;

  return (
    <section className="kx-card" aria-label={t('schedule.availTitle')}>
      <div className="kx-card__head">
        <h2><IconCalendar className="kx-title-ic" size={18} />{t('schedule.availMonth', { y: year, m: month + 1 })}</h2>
        {data?.degraded && <span className="kx-bi__degraded">{t('schedule.offlineSample')}</span>}
      </div>
      {query.isLoading ? (
        <Skeleton height={80} />
      ) : (
        <>
          {data && data.hallCount > 0 && (
            <p className="kx-sched__count tnum">{t('schedule.availHalls', { n: data.hallCount })}</p>
          )}
          <ul className="kx-availlist">
            {(data?.rows ?? []).map((r) => (
              <li key={r.label} className="kx-availlist__item">
                <span className="kx-availlist__label">{r.label}</span>
                <span className="kx-availlist__meta tnum">
                  {r.totalDays > 0 ? t('schedule.availFreeDays', { free: r.freeDays, total: r.totalDays }) : ''}
                </span>
                <span className={`kx-availbadge ${r.available ? 'is-open' : 'is-full'}`}>
                  {r.available ? t('schedule.availOpen') : t('schedule.availFull')}
                </span>
              </li>
            ))}
          </ul>
        </>
      )}
    </section>
  );
}

function HallUtilization({ cursor }: { cursor: Date }) {
  const { t } = useTranslation();
  const year = cursor.getFullYear();
  const month = cursor.getMonth();

  /*
   * 실 원천: `GET /api/events/hall-utilization?year=&month=` — event + hall_assignment/hall_label 집계.
   * 실패(NETWORK/NOT_FOUND/NOT_IMPLEMENTED) 시에만 크롤링 샘플(hallUtilizationFor)로 폴백.
   */
  const query = useQuery<{ rows: HallUtilRow[]; degraded: boolean }>({
    queryKey: ['schedule-hall-util', year, month],
    queryFn: async () => {
      try {
        const data = await scheduleApi.hallUtilization(year, month + 1);
        return { rows: data.map((h) => ({ label: h.label, pct: h.pct })), degraded: false };
      } catch (err) {
        if (isDegradable(err)) return { rows: hallUtilizationFor(year, month), degraded: true };
        throw err;
      }
    },
  });
  const rows = query.data?.rows ?? [];
  const degraded = query.data?.degraded ?? false;

  return (
    <section className="kx-card" aria-label={t('schedule.hallUtil')}>
      <div className="kx-card__head">
        <h2><IconGrid className="kx-title-ic" size={18} />{t('schedule.hallUtilMonth', { y: year, m: month + 1 })}</h2>
        {degraded && <span className="kx-bi__degraded">{t('schedule.offlineSample')}</span>}
      </div>
      <ul className="kx-barlist">
        {rows.map((h) => (
          <li key={h.label} className="kx-barlist__item">
            <div className="kx-barlist__top">
              <span>{h.label}</span>
              <span className="tnum">{h.pct}%</span>
            </div>
            <div className="kx-barlist__track">
              <span className="kx-barlist__fill" style={{ width: `${h.pct}%` }} />
            </div>
          </li>
        ))}
      </ul>
    </section>
  );
}
