/*
 * KxDataTable — 재사용 데이터테이블 (Nifty tables/tabulator 계보 · 소유자 지시 1.3 / task #32).
 *
 * 정본 `.kx-table`(screens/shared.css) 스타일 위에 인터랙션 레이어를 얹은 자체 구현.
 * 외부 라이브러리(tabulator-tables 등) 미사용 — 번들·다크·i18n·접근성 정합을 위해 직접 구현.
 *
 * 기능: 컬럼 헤더 클릭 정렬(asc/desc/해제)·텍스트 필터(검색)·페이지네이션(10/25/50)·
 *      CSV 내보내기(UTF-8 BOM · 한글 엑셀 호환 · CRLF · 셀 이스케이프).
 *
 * 데이터 소싱은 호출측 책임 — rows 는 이미 (서버/외부필터로) 확정된 배열을 넘긴다.
 * 외부 세그먼트/칩 필터는 toolbarStart 로 주입(검색/정렬/페이지는 이 컴포넌트가 담당).
 *
 * 사용 예:
 *   <KxDataTable
 *     columns={[{ key:'name', header:t('...'), value:(r)=>r.name },
 *               { key:'amount', header:t('...'), numeric:true, value:(r)=>r.amount,
 *                 render:(r)=><span className="tnum">{r.amount.toLocaleString()}</span> }]}
 *     rows={rows} rowKey={(r)=>r.id} onRowClick={setSel} csvFileName="exhibitors" />
 */
import { useMemo, useState, type ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { EmptyState } from './ui/States';
import { IconDownload, IconSearch, IconSort } from './ui/icons';
import './data-table.css';

export type KxCellValue = string | number | null | undefined;

export interface KxColumn<T> {
  /** 안정 키(정렬·React key). */
  key: string;
  /** 헤더 라벨(이미 i18n 해소된 문자열). */
  header: string;
  /** 정렬·검색·CSV 에 쓰는 원시 값 접근자. 없으면 그 컬럼은 정렬/검색/CSV 제외. */
  value?: (row: T) => KxCellValue;
  /** 셀 렌더(미지정 시 value 문자열 표시). */
  render?: (row: T) => ReactNode;
  /** 숫자열 — 우정렬 tabular-nums(.kx-num) + 수치 비교 정렬. */
  numeric?: boolean;
  /** 정렬 가능 여부(기본: value 가 있으면 true). */
  sortable?: boolean;
  /** 검색 대상 여부(기본: value 가 있으면 true). */
  searchable?: boolean;
  /** CSV 포함 여부(기본 true). */
  csv?: boolean;
  /** 셀(td) 추가 클래스. */
  tdClassName?: string;
  /** 헤더(th) 추가 클래스. */
  thClassName?: string;
}

type SortDir = 'asc' | 'desc';
interface SortState {
  key: string;
  dir: SortDir;
}

export interface KxDataTableProps<T> {
  columns: KxColumn<T>[];
  rows: T[];
  rowKey: (row: T) => string;
  onRowClick?: (row: T) => void;
  isRowSelected?: (row: T) => boolean;
  rowClassName?: (row: T) => string | undefined;
  /** 제브라 스트라이프(기본 true). */
  zebra?: boolean;
  /** 검색 입력 노출(기본 true). */
  searchable?: boolean;
  searchPlaceholder?: string;
  /** 페이지 크기 선택지(기본 [10,25,50]). */
  pageSizeOptions?: number[];
  initialPageSize?: number;
  /** 지정 시 CSV 내보내기 버튼 노출 — 다운로드 파일명(확장자 제외). */
  csvFileName?: string;
  /** 툴바 좌측(외부 필터 등) · 우측 추가 슬롯. */
  toolbarStart?: ReactNode;
  toolbarEnd?: ReactNode;
  ariaLabel?: string;
  emptyTitle?: string;
  emptyDescription?: string;
  className?: string;
}

/** CSV 셀 이스케이프(쉼표·따옴표·줄바꿈 방어). */
function csvCell(v: KxCellValue): string {
  const s = String(v ?? '');
  return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
}

/** 값 → 검색 문자열(소문자). */
function searchText(v: KxCellValue): string {
  return String(v ?? '').toLowerCase();
}

/** 두 셀 값 비교(numeric 이면 수치, 아니면 로케일 문자열). null/undefined 는 뒤로. */
function compare(a: KxCellValue, b: KxCellValue, numeric: boolean): number {
  const an = a === null || a === undefined || a === '';
  const bn = b === null || b === undefined || b === '';
  if (an && bn) return 0;
  if (an) return 1;
  if (bn) return -1;
  if (numeric) return Number(a) - Number(b);
  return String(a).localeCompare(String(b), undefined, { numeric: true });
}

export function KxDataTable<T>({
  columns,
  rows,
  rowKey,
  onRowClick,
  isRowSelected,
  rowClassName,
  zebra = true,
  searchable = true,
  searchPlaceholder,
  pageSizeOptions = [10, 25, 50],
  initialPageSize,
  csvFileName,
  toolbarStart,
  toolbarEnd,
  ariaLabel,
  emptyTitle,
  emptyDescription,
  className = '',
}: KxDataTableProps<T>) {
  const { t } = useTranslation();
  const [query, setQuery] = useState('');
  const [sort, setSort] = useState<SortState | null>(null);
  const [pageSize, setPageSize] = useState(initialPageSize ?? pageSizeOptions[0] ?? 10);
  const [page, setPage] = useState(0);

  const colOf = (key: string) => columns.find((c) => c.key === key);
  const canSort = (c: KxColumn<T>) => (c.sortable ?? !!c.value) && !!c.value;
  const canSearch = (c: KxColumn<T>) => (c.searchable ?? !!c.value) && !!c.value;

  // ── 필터(검색) ──
  const filtered = useMemo(() => {
    const kw = query.trim().toLowerCase();
    if (!kw) return rows;
    const searchCols = columns.filter(canSearch);
    return rows.filter((r) =>
      searchCols.some((c) => searchText(c.value!(r)).includes(kw)),
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [rows, query, columns]);

  // ── 정렬 ──
  const sorted = useMemo(() => {
    if (!sort) return filtered;
    const col = colOf(sort.key);
    if (!col?.value) return filtered;
    const dir = sort.dir === 'asc' ? 1 : -1;
    // 안정 정렬: 원본 인덱스 tie-break.
    return filtered
      .map((r, i) => [r, i] as const)
      .sort((x, y) => {
        const c = compare(col.value!(x[0]), col.value!(y[0]), !!col.numeric) * dir;
        return c !== 0 ? c : x[1] - y[1];
      })
      .map(([r]) => r);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [filtered, sort, columns]);

  // ── 페이지네이션(page 는 렌더 시 유효 범위로 클램프) ──
  const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));
  const safePage = Math.min(page, totalPages - 1);
  const start = safePage * pageSize;
  const pageRows = sorted.slice(start, start + pageSize);

  const onSearch = (v: string) => {
    setQuery(v);
    setPage(0);
  };
  const onPageSize = (v: number) => {
    setPageSize(v);
    setPage(0);
  };
  const toggleSort = (key: string) => {
    setPage(0);
    setSort((prev) => {
      if (!prev || prev.key !== key) return { key, dir: 'asc' };
      if (prev.dir === 'asc') return { key, dir: 'desc' };
      return null; // desc → 해제
    });
  };

  const exportCsv = () => {
    const csvCols = columns.filter((c) => c.value && c.csv !== false);
    const header = csvCols.map((c) => csvCell(c.header)).join(',');
    const lines = sorted.map((r) => csvCols.map((c) => csvCell(c.value!(r))).join(','));
    // UTF-8 BOM(﻿) + CRLF — 한글 엑셀 호환.
    const csv = '﻿' + [header, ...lines].join('\r\n');
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `${csvFileName ?? 'export'}.csv`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const showToolbar = searchable || !!csvFileName || !!toolbarStart || !!toolbarEnd;

  return (
    <div className={`kx-dt ${className}`}>
      {showToolbar && (
        <div className="kx-dt__toolbar">
          {toolbarStart}
          {searchable && (
            <label className="kx-dt__search">
              <IconSearch size={16} className="kx-dt__search-ic" />
              <input
                className="kx-dt__search-input"
                type="search"
                value={query}
                placeholder={searchPlaceholder ?? t('datatable.search')}
                onChange={(e) => onSearch(e.target.value)}
                aria-label={searchPlaceholder ?? t('datatable.searchAria')}
              />
            </label>
          )}
          <div className="kx-dt__toolbar-end">
            {toolbarEnd}
            {csvFileName && (
              <button
                type="button"
                className="kx-dt__csv"
                onClick={exportCsv}
                disabled={sorted.length === 0}
              >
                <IconDownload size={16} />
                {t('datatable.csv')}
              </button>
            )}
          </div>
        </div>
      )}

      <div className="kx-table-scroll">
        <table
          className={`kx-table ${zebra ? 'kx-table--zebra' : ''}`}
          aria-label={ariaLabel}
        >
          <thead>
            <tr>
              {columns.map((c) => {
                const sortable = canSort(c);
                const active = sort?.key === c.key;
                const dir = active ? sort!.dir : undefined;
                const ariaSort = active ? (dir === 'asc' ? 'ascending' : 'descending') : 'none';
                return (
                  <th
                    key={c.key}
                    className={`${c.numeric ? 'kx-num' : ''} ${c.thClassName ?? ''}`}
                    aria-sort={sortable ? (ariaSort as 'ascending' | 'descending' | 'none') : undefined}
                    scope="col"
                  >
                    {sortable ? (
                      <button
                        type="button"
                        className={`kx-dt__sort-btn ${c.numeric ? 'kx-dt__sort-btn--num' : ''}`}
                        data-dir={dir ?? 'none'}
                        onClick={() => toggleSort(c.key)}
                        title={
                          dir === 'asc'
                            ? t('datatable.sortDesc')
                            : dir === 'desc'
                              ? t('datatable.sortClear')
                              : t('datatable.sortAsc')
                        }
                      >
                        <span className="kx-dt__sort-label">{c.header}</span>
                        <IconSort size={14} className="kx-dt__sort-ic" aria-hidden="true" />
                      </button>
                    ) : (
                      c.header
                    )}
                  </th>
                );
              })}
            </tr>
          </thead>
          <tbody>
            {pageRows.map((r) => {
              const selected = isRowSelected?.(r);
              const extra = rowClassName?.(r) ?? '';
              return (
                <tr
                  key={rowKey(r)}
                  className={`${onRowClick ? 'kx-dt__row--click' : ''} ${selected ? 'is-selected' : ''} ${extra}`}
                  aria-selected={isRowSelected ? !!selected : undefined}
                  tabIndex={onRowClick ? 0 : undefined}
                  onClick={onRowClick ? () => onRowClick(r) : undefined}
                  onKeyDown={
                    onRowClick
                      ? (e) => {
                          if (e.key === 'Enter' || e.key === ' ') {
                            e.preventDefault();
                            onRowClick(r);
                          }
                        }
                      : undefined
                  }
                >
                  {columns.map((c) => (
                    <td key={c.key} className={`${c.numeric ? 'kx-num tnum' : ''} ${c.tdClassName ?? ''}`}>
                      {c.render ? c.render(r) : (c.value ? String(c.value(r) ?? '-') : null)}
                    </td>
                  ))}
                </tr>
              );
            })}
            {pageRows.length === 0 && (
              <tr>
                <td colSpan={columns.length} className="kx-dt__empty-cell">
                  <EmptyState
                    title={emptyTitle ?? t('datatable.emptyTitle')}
                    description={emptyDescription ?? (query ? t('datatable.emptyFiltered') : undefined)}
                  />
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </div>

      <div className="kx-dt__footer">
        <span className="kx-dt__count tnum">
          {t('datatable.count', { shown: pageRows.length, total: sorted.length })}
        </span>
        <div className="kx-dt__pager">
          <label className="kx-dt__pagesize">
            <span className="kx-dt__pagesize-label">{t('datatable.pageSize')}</span>
            <select
              className="kx-select kx-dt__pagesize-select"
              value={pageSize}
              onChange={(e) => onPageSize(Number(e.target.value))}
              aria-label={t('datatable.pageSize')}
            >
              {pageSizeOptions.map((n) => (
                <option key={n} value={n}>
                  {n}
                </option>
              ))}
            </select>
          </label>
          <div className="kx-dt__pagenav">
            <button
              type="button"
              className="kx-dt__page-btn"
              disabled={safePage <= 0}
              onClick={() => setPage(Math.max(0, safePage - 1))}
            >
              {t('datatable.prev')}
            </button>
            <span className="kx-dt__pageinfo tnum">
              {t('datatable.pageInfo', { page: safePage + 1, total: totalPages })}
            </span>
            <button
              type="button"
              className="kx-dt__page-btn"
              disabled={safePage + 1 >= totalPages}
              onClick={() => setPage(safePage + 1)}
            >
              {t('datatable.next')}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}
