"use client";

import { ReactNode, Fragment, useState, useEffect, useRef, useCallback } from "react";
import { LucideIcon, Trash2, Plus, Loader2, ArrowUpDown, ArrowUp, ArrowDown, Search } from "lucide-react";
import { DateRangeSelector } from "@/components/molecules/DateRangeSelector";
import { Card, CardContent } from "@/components/ui/card";
import { Tabs, TabsContent } from "@/components/ui/tabs";
import { TableSkeleton, MobileCardGridSkeleton, StatCardGridSkeleton, ListPageSkeleton } from "@/components/ui/skeleton";
import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Checkbox } from "@/components/ui/checkbox";
import { Button } from "@/components/ui/button";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog';
import { Input } from "@/components/ui/input";
import { PageLayout } from "@/components/organisms/PageLayout";
import { PageHeader } from "@/components/organisms/PageHeader";
import { StatCards } from "@/components/organisms/StatCards";
import { SearchFilter } from "@/components/organisms/SearchFilter";
import { ScreenVersionHistory } from "@/components/organisms/ScreenVersionHistory";
import { TabChip } from "@/components/atoms/TabChip";
import { MultiSelectCombobox } from "@/components/ui/multi-select-combobox";
import { MobileFilter, FilterFieldConfig, FilterValues } from "@/components/molecules/MobileFilter";

/**
 * 기본 통합 목록_버젼2
 *
 * 품목관리 스타일의 완전한 목록 템플릿
 * - PageHeader, StatCards, SearchFilter, ScreenVersionHistory
 * - 탭 기반 필터 (데스크톱: TabsList, 모바일: 커스텀 버튼)
 * - 체크박스 포함 DataTable (Desktop)
 * - 체크박스 포함 모바일 카드 (Mobile)
 * - 페이지네이션
 */

export interface TabOption {
  value: string;
  label: string;
  count: number;
  color?: string; // 모바일 탭 색상
}

export interface TableColumn {
  key: string;
  label: string;
  className?: string;
  hideOnMobile?: boolean;
  hideOnTablet?: boolean;
  /** 정렬 가능 여부 */
  sortable?: boolean;
}

export interface PaginationConfig {
  currentPage: number;
  totalPages: number;
  totalItems: number;
  itemsPerPage: number;
  onPageChange: (page: number) => void;
}

export interface StatCard {
  label: string;
  value: string | number;
  icon: LucideIcon;
  iconColor: string;
  onClick?: () => void;
  isActive?: boolean;
}

export interface VersionHistoryItem {
  version: string;
  description: string;
  modifiedBy: string;
  modifiedAt: string;
}

export interface DevMetadata {
  componentName: string;
  pagePath: string;
  description: string;
  apis?: any[];
  dataStructures?: any[];
  dbSchema?: any[];
  businessLogic?: any[];
}

export interface IntegratedListTemplateV2Props<T = any> {
  // 페이지 헤더
  title: string;
  description?: string;
  icon?: LucideIcon;
  headerActions?: ReactNode;

  // ===== 공통 헤더 옵션 (달력/등록버튼) =====
  /**
   * 날짜 범위 선택기 (왼쪽 배치)
   * - enabled: 달력 표시 여부
   * - showPresets: 프리셋 버튼 (당해년도, 전전월, 전월, 당월, 어제, 오늘)
   * - startDate/endDate: 외부 상태 연동
   * - onChange: 날짜 변경 콜백
   */
  dateRangeSelector?: {
    enabled: boolean;
    showPresets?: boolean;
    /** 날짜 입력 숨김 (검색창만 표시하고 싶을 때) */
    hideDateInputs?: boolean;
    startDate?: string;
    endDate?: string;
    onStartDateChange?: (date: string) => void;
    onEndDateChange?: (date: string) => void;
    /** 추가 액션 (검색창 등) - 프리셋 버튼 옆에 배치 */
    extraActions?: ReactNode;
  };
  /**
   * 등록 버튼 (오른쪽 끝 배치)
   * - label: 버튼 텍스트 (예: '등록', '공정 등록')
   * - onClick: 클릭 핸들러
   * - icon: 아이콘 (기본: Plus)
   */
  createButton?: {
    label: string;
    onClick: () => void;
    icon?: LucideIcon;
  };

  // 탭 콘텐츠 (헤더 액션 아래, 검색 위에 표시되는 커스텀 탭)
  tabsContent?: ReactNode;

  // 통계 카드
  stats?: StatCard[];

  // 경고 배너 (통계 카드와 검색 영역 사이)
  alertBanner?: ReactNode;

  // 버전 이력
  versionHistory?: VersionHistoryItem[];
  versionHistoryTitle?: string;

  // 검색 및 필터
  searchValue?: string;
  onSearchChange?: (value: string) => void;
  searchPlaceholder?: string;
  extraFilters?: ReactNode; // Select, DatePicker 등 추가 필터
  hideSearch?: boolean; // 검색창 숨김 여부

  // 탭 (품목 유형, 상태 등) - optional
  tabs?: TabOption[];
  activeTab?: string;
  onTabChange?: (value: string) => void;

  // 테이블 헤더 액션 (탭 옆에 표시될 셀렉트박스 등)
  tableHeaderActions?: ReactNode;

  // 모바일/카드 뷰용 필터 슬롯 (xl 미만에서 카드 목록 위에 표시)
  mobileFilterSlot?: ReactNode;

  // ===== 새로운 통합 필터 시스템 (선택적 사용) =====
  // filterConfig를 전달하면 PC는 인라인, 모바일은 바텀시트로 자동 분기
  // 기존 tableHeaderActions, mobileFilterSlot과 함께 사용 가능
  filterConfig?: FilterFieldConfig[];
  filterValues?: FilterValues;
  onFilterChange?: (key: string, value: string | string[]) => void;
  onFilterReset?: () => void;
  filterTitle?: string; // 모바일 필터 바텀시트 제목 (기본: "검색 필터")

  // 테이블 앞에 표시될 컨텐츠 (계정과목명 + 저장 버튼 등)
  beforeTableContent?: ReactNode;

  // 테이블 컬럼
  tableColumns: TableColumn[];
  tableTitle?: string; // "전체 목록 (100개)" 같은 타이틀

  // ===== 정렬 설정 =====
  /** 현재 정렬 컬럼 키 */
  sortBy?: string;
  /** 정렬 방향 */
  sortOrder?: 'asc' | 'desc';
  /** 정렬 변경 핸들러 */
  onSort?: (key: string) => void;

  // 커스텀 테이블 헤더 렌더링 (동적 컬럼용)
  renderCustomTableHeader?: () => ReactNode;

  // 테이블 하단 푸터 (합계 등)
  tableFooter?: ReactNode;

  // 데이터
  data: T[]; // 데스크톱용 페이지네이션된 데이터
  totalCount?: number; // 전체 데이터 개수 (역순 번호 계산용)
  allData?: T[]; // 클라이언트 사이드 필터링용 전체 데이터 (소량 데이터 페이지용)
  mobileDisplayCount?: number; // 클라이언트 사이드 인피니티에서 표시할 개수
  onLoadMore?: () => void; // 더 불러오기 콜백 (레거시)

  // ===== 서버 사이드 모바일 인피니티 스크롤 =====
  // 모바일에서 스크롤/버튼으로 다음 페이지 로드, 데이터 누적 표시
  enableMobileInfinityScroll?: boolean; // 서버 사이드 인피니티 활성화 (기본: true)
  isMobileLoading?: boolean; // 모바일 추가 로딩 중 상태

  // 체크박스 선택
  selectedItems: Set<string>;
  onToggleSelection: (id: string) => void;
  onToggleSelectAll: () => void;
  getItemId: (item: T) => string; // 아이템에서 ID 추출
  onBulkDelete?: () => void; // 일괄 삭제 핸들러

  // 테이블 표시 옵션
  showCheckbox?: boolean; // 체크박스 표시 여부 (기본: true)
  showRowNumber?: boolean; // 번호 컬럼 표시 여부 (기본: true, tableColumns에 번호 포함 시)

  // 렌더링 함수
  renderTableRow: (item: T, index: number, globalIndex: number) => ReactNode;
  renderMobileCard: (item: T, index: number, globalIndex: number, isSelected: boolean, onToggle: () => void) => ReactNode;

  // 페이지네이션
  pagination: PaginationConfig;

  // 개발자 메타데이터
  devMetadata?: DevMetadata;

  // 로딩 상태
  isLoading?: boolean;
}

export function IntegratedListTemplateV2<T = any>({
  title,
  description,
  icon,
  headerActions,
  dateRangeSelector,
  createButton,
  tabsContent,
  stats,
  alertBanner,
  versionHistory,
  versionHistoryTitle = "수정 이력",
  searchValue,
  onSearchChange,
  searchPlaceholder = "검색...",
  extraFilters,
  hideSearch = true, // 기본값: 타이틀 아래에 검색창 표시 (Card 안 SearchFilter 숨김)
  tabs,
  activeTab,
  onTabChange,
  tableHeaderActions,
  mobileFilterSlot,
  filterConfig,
  filterValues,
  onFilterChange,
  onFilterReset,
  filterTitle = "검색 필터",
  beforeTableContent,
  tableColumns,
  tableTitle,
  sortBy,
  sortOrder,
  onSort,
  renderCustomTableHeader,
  tableFooter,
  data,
  totalCount,
  allData,
  mobileDisplayCount,
  onLoadMore,
  enableMobileInfinityScroll = true, // 기본값: 활성화
  isMobileLoading = false,
  selectedItems,
  onToggleSelection,
  onToggleSelectAll,
  getItemId,
  onBulkDelete,
  showCheckbox = true, // 기본값 true
  showRowNumber = true, // 기본값 true (번호 컬럼은 renderTableRow에서 처리)
  renderTableRow,
  renderMobileCard,
  pagination,
  devMetadata,
  isLoading,
}: IntegratedListTemplateV2Props<T>) {

  const [showDeleteDialog, setShowDeleteDialog] = useState(false);

  // ===== 서버 사이드 모바일 인피니티 스크롤 =====
  // 모바일에서 누적 데이터를 관리하여 스크롤 시 계속 추가
  const [accumulatedMobileData, setAccumulatedMobileData] = useState<T[]>([]);
  const [lastAccumulatedPage, setLastAccumulatedPage] = useState(0);
  const mobileScrollSentinelRef = useRef<HTMLDivElement>(null);

  // 클라이언트 사이드 인피니티용 (allData가 있는 경우)
  const [clientDisplayCount, setClientDisplayCount] = useState(mobileDisplayCount || 20);

  // 서버 페이지네이션: 데이터 누적 로직
  // - 페이지 1이면 리셋 (필터/탭 변경)
  // - 이전 페이지 + 1이면 누적 (스크롤로 다음 페이지 로드)
  //
  // 서버 사이드 판단: allData가 없거나, pagination.totalItems > allData.length
  // (외부 훅으로 데이터 관리하면서 서버 페이지네이션 사용하는 경우)
  useEffect(() => {
    const isServerSide = !allData || pagination.totalItems > allData.length;

    if (!isServerSide) {
      // 순수 클라이언트 사이드 필터링 모드 - 누적 불필요
      return;
    }

    if (!enableMobileInfinityScroll) {
      // 서버 사이드 인피니티 비활성화 - data만 사용
      setAccumulatedMobileData(data);
      return;
    }

    if (pagination.currentPage === 1) {
      // 페이지 1: 필터/탭 변경으로 리셋
      setAccumulatedMobileData(data);
      setLastAccumulatedPage(1);
    } else if (pagination.currentPage === lastAccumulatedPage + 1) {
      // 다음 페이지: 기존 데이터에 누적 (중복 제거)
      setAccumulatedMobileData(prev => {
        const existingIds = new Set(prev.map(item => getItemId(item)));
        const newItems = data.filter(item => !existingIds.has(getItemId(item)));
        return [...prev, ...newItems];
      });
      setLastAccumulatedPage(pagination.currentPage);
    } else if (pagination.currentPage !== lastAccumulatedPage) {
      // 페이지 점프 (예: PC에서 페이지 변경 후 모바일로): 현재 데이터만 표시
      setAccumulatedMobileData(data);
      setLastAccumulatedPage(pagination.currentPage);
    }
  }, [data, pagination.currentPage, pagination.totalItems, allData, enableMobileInfinityScroll, lastAccumulatedPage, getItemId]);

  // 탭 변경 감지: activeTab 변경 시 누적 데이터 리셋
  // 주의: allData를 dependency에 넣으면 페이지 변경 시마다 리셋됨 (외부 훅 사용 시)
  useEffect(() => {
    if (enableMobileInfinityScroll) {
      setAccumulatedMobileData([]);
      setLastAccumulatedPage(0);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeTab]); // activeTab만 감지 - 탭 변경 시에만 리셋

  // 클라이언트 사이드: allData가 변경되면 displayCount 리셋
  useEffect(() => {
    if (allData) {
      setClientDisplayCount(mobileDisplayCount || 20);
    }
  }, [allData, mobileDisplayCount]);

  // 서버 사이드: 스크롤/버튼으로 다음 페이지 로드
  const handleLoadMoreMobile = useCallback(() => {
    if (isMobileLoading) return;
    if (pagination.currentPage >= pagination.totalPages) return;

    // 다음 페이지 요청
    pagination.onPageChange(pagination.currentPage + 1);
  }, [isMobileLoading, pagination]);

  // 클라이언트 사이드: 더 보기
  const handleLoadMoreClient = useCallback(() => {
    if (!allData) return;
    setClientDisplayCount(prev => Math.min(prev + 20, allData.length));
    onLoadMore?.();
  }, [allData, onLoadMore]);

  // 서버 사이드 페이지네이션 사용 여부 판단 (useEffect보다 먼저 정의)
  // - allData가 없으면 서버 사이드
  // - allData가 있어도 pagination.totalItems가 더 크면 서버 사이드 (외부 훅으로 데이터 관리하는 경우)
  const isServerSidePagination = !allData || pagination.totalItems > allData.length;

  // Intersection Observer - 서버 사이드 & 클라이언트 사이드 공통
  useEffect(() => {
    if (!mobileScrollSentinelRef.current) return;

    const observer = new IntersectionObserver(
      (entries) => {
        if (!entries[0].isIntersecting) return;

        if (isServerSidePagination) {
          // 서버 사이드 인피니티
          if (enableMobileInfinityScroll && !isMobileLoading && pagination.currentPage < pagination.totalPages) {
            handleLoadMoreMobile();
          }
        } else {
          // 클라이언트 사이드 인피니티
          if (clientDisplayCount < allData!.length) {
            handleLoadMoreClient();
          }
        }
      },
      { threshold: 0.1 }
    );

    observer.observe(mobileScrollSentinelRef.current);
    return () => observer.disconnect();
  }, [isServerSidePagination, allData, clientDisplayCount, enableMobileInfinityScroll, isMobileLoading, pagination.currentPage, pagination.totalPages, handleLoadMoreClient, handleLoadMoreMobile]);

  const startIndex = (pagination.currentPage - 1) * pagination.itemsPerPage;
  const allSelected = selectedItems.size === data.length && data.length > 0;

  // 모바일용 데이터 결정
  // 1. 서버 사이드: 누적 데이터 또는 현재 페이지 데이터
  // 2. 클라이언트 사이드: allData에서 slice
  // 참고: accumulatedMobileData가 비어있으면 data 또는 allData를 폴백으로 사용
  const mobileData = isServerSidePagination
    ? (enableMobileInfinityScroll
        ? (accumulatedMobileData.length > 0 ? accumulatedMobileData : (allData || data))
        : data)
    : allData!.slice(0, clientDisplayCount);

  // 더 로드 가능 여부
  const hasMoreData = isServerSidePagination
    ? pagination.currentPage < pagination.totalPages
    : clientDisplayCount < allData!.length;

  // 현재 로드된 개수 / 전체 개수
  const loadedCount = isServerSidePagination
    ? (accumulatedMobileData.length > 0 ? accumulatedMobileData.length : mobileData.length)
    : clientDisplayCount;
  const totalDataCount = isServerSidePagination
    ? pagination.totalItems
    : allData!.length;

  // ===== filterConfig 기반 자동 필터 렌더링 =====
  // PC용 인라인 필터 (xl 이상에서 표시)
  const renderAutoFilters = () => {
    if (!filterConfig || !filterValues || !onFilterChange) return null;

    return (
      <div className="flex items-center gap-2 flex-wrap">
        {filterConfig.map((field) => {
          if (field.type === 'single') {
            // 단일선택: Select
            return (
              <Select
                key={field.key}
                value={(filterValues[field.key] as string) || 'all'}
                onValueChange={(value) => onFilterChange(field.key, value)}
              >
                <SelectTrigger className="w-[120px]">
                  <SelectValue placeholder={field.allOptionLabel || field.label} />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="all">
                    {field.allOptionLabel || '전체'}
                  </SelectItem>
                  {field.options.map((option) => (
                    <SelectItem key={option.value} value={option.value}>
                      {option.label}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            );
          } else {
            // 다중선택: MultiSelectCombobox
            return (
              <MultiSelectCombobox
                key={field.key}
                options={field.options.map((opt) => ({
                  value: opt.value,
                  label: opt.label,
                }))}
                value={(filterValues[field.key] as string[]) || []}
                onChange={(value) => onFilterChange(field.key, value)}
                placeholder={field.label}
                searchPlaceholder={`${field.label} 검색...`}
                className="w-[140px]"
              />
            );
          }
        })}
      </div>
    );
  };

  // 모바일용 바텀시트 필터 (xl 미만에서 표시)
  const renderAutoMobileFilter = () => {
    if (!filterConfig || !filterValues || !onFilterChange || !onFilterReset) return null;

    return (
      <MobileFilter
        fields={filterConfig}
        values={filterValues}
        onChange={onFilterChange}
        onReset={onFilterReset}
        buttonLabel="필터"
        title={filterTitle}
      />
    );
  };

  // 일괄삭제 확인 핸들러
  const handleBulkDeleteClick = () => {
    setShowDeleteDialog(true);
  };

  // 일괄삭제 실행
  const handleConfirmDelete = () => {
    if (onBulkDelete) {
      onBulkDelete();
    }
    setShowDeleteDialog(false);
  };

  // 헤더 액션 스켈레톤 (달력 + 프리셋 버튼 + 등록 버튼)
  const renderHeaderActionSkeleton = () => (
    <div className="flex items-center gap-2 flex-wrap w-full">
      {dateRangeSelector?.enabled && (
        <>
          <div className="flex items-center gap-2">
            <div className="h-10 w-[140px] rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
            <div className="h-4 w-4 bg-gray-300 rounded animate-pulse" />
            <div className="h-10 w-[140px] rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
          </div>
          <div className="hidden md:flex items-center gap-1.5">
            <div className="h-8 w-16 rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
            <div className="h-8 w-14 rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
            <div className="h-8 w-12 rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
            <div className="h-8 w-12 rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
            <div className="h-8 w-10 rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
            <div className="h-8 w-10 rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
          </div>
        </>
      )}
      {createButton && (
        <div className="h-10 w-28 rounded-md border border-gray-200 bg-gray-100 animate-pulse ml-auto" />
      )}
    </div>
  );

  return (
    <PageLayout>
      {/* 페이지 헤더 - 항상 표시 */}
      <PageHeader
        title={title}
        description={description}
        icon={icon}
      />

      {/* 헤더 액션 (달력, 버튼 등) - 타이틀 아래 배치 */}
      {/* 레이아웃: [달력] [프리셋버튼] [검색창] -------------- [추가버튼들] [등록버튼] (오른쪽 끝) */}
      {(dateRangeSelector?.enabled || createButton || headerActions || (hideSearch && onSearchChange)) && (
        isLoading ? renderHeaderActionSkeleton() : (
          <div className="flex flex-col xl:flex-row xl:items-center gap-2 w-full">
            {/* 날짜 범위 선택기 + 검색창 (왼쪽) */}
            {dateRangeSelector?.enabled ? (
              <DateRangeSelector
                startDate={dateRangeSelector.startDate || ''}
                endDate={dateRangeSelector.endDate || ''}
                onStartDateChange={dateRangeSelector.onStartDateChange}
                onEndDateChange={dateRangeSelector.onEndDateChange}
                hidePresets={dateRangeSelector.showPresets === false}
                hideDateInputs={dateRangeSelector.hideDateInputs}
                extraActions={
                  <>
                    {/* hideSearch=true면 검색창 자동 추가 (extraActions 앞에) */}
                    {hideSearch && onSearchChange && (
                      <div className="relative w-full xl:w-[300px]">
                        <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
                        <Input
                          type="text"
                          placeholder={searchPlaceholder}
                          value={searchValue || ''}
                          onChange={(e) => onSearchChange(e.target.value)}
                          className="pl-9 w-full bg-gray-50 border-gray-200"
                        />
                      </div>
                    )}
                    {/* 기존 extraActions (추가 버튼 등) */}
                    {dateRangeSelector.extraActions}
                  </>
                }
              />
            ) : (
              /* dateRangeSelector 없어도 hideSearch=true면 검색창 표시 */
              hideSearch && onSearchChange && (
                <div className="relative w-full xl:w-[300px]">
                  <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
                  <Input
                    type="text"
                    placeholder={searchPlaceholder}
                    value={searchValue || ''}
                    onChange={(e) => onSearchChange(e.target.value)}
                    className="pl-9 w-full bg-gray-50 border-gray-200"
                  />
                </div>
              )
            )}
            {/* 버튼 영역 (오른쪽 끝으로 통합) */}
            {(headerActions || createButton) && (
              <div className="flex items-center gap-2 ml-auto shrink-0">
                {/* 헤더 액션 (엑셀 다운로드 등 추가 버튼들) */}
                {headerActions}
                {/* 등록 버튼 */}
                {createButton && (
                  <Button onClick={createButton.onClick}>
                    {createButton.icon ? (
                      <createButton.icon className="h-4 w-4 mr-2" />
                    ) : (
                      <Plus className="h-4 w-4 mr-2" />
                    )}
                    {createButton.label}
                  </Button>
                )}
              </div>
            )}
          </div>
        )
      )}

      {/* 커스텀 탭 콘텐츠 (헤더 아래, 검색 위) */}
      {tabsContent && (
        <div className="flex items-center">
          {tabsContent}
        </div>
      )}

      {/* 통계 카드 - 태블릿/데스크톱 */}
      {isLoading && stats !== undefined ? (
        <div className="hidden md:block">
          <StatCardGridSkeleton count={stats.length || 4} />
        </div>
      ) : stats && stats.length > 0 ? (
        <div className="hidden md:block">
          <StatCards stats={stats} />
        </div>
      ) : null}

      {/* 경고 배너 (통계 카드와 검색 영역 사이) */}
      {alertBanner}

      {/* 버전 이력 */}
      {versionHistory && versionHistory.length > 0 && (
        <ScreenVersionHistory
          versionHistory={versionHistory as any}
          title={versionHistoryTitle}
        />
      )}

      {/* 검색 및 필터 */}
      {!hideSearch && (
        <Card>
          <CardContent className="p-6">
            {isLoading ? (
              <div className="flex flex-wrap gap-4">
                <div className="h-10 w-64 rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
                <div className="h-10 w-32 rounded-md border border-gray-200 bg-gray-100 animate-pulse" />
              </div>
            ) : (
              <SearchFilter
                searchValue={searchValue || ''}
                onSearchChange={onSearchChange || (() => {})}
                searchPlaceholder={searchPlaceholder}
                filterButton={false}
                extraActions={extraFilters}
              />
            )}
          </CardContent>
        </Card>
      )}

      {/* 테이블 앞 컨텐츠 (계정과목명 + 저장 버튼, 달력 등) */}
      {beforeTableContent && (
        <div className="w-full py-2">
          {beforeTableContent}
        </div>
      )}

      {/* 목록 카드 */}
      <Card>
        <CardContent className="pt-6">
          <Tabs value={activeTab || 'default'} onValueChange={onTabChange} className="w-full">
            {/* 데스크톱 (1280px+) - TabChip 탭 */}
            <div className="hidden xl:block mb-4">
              <div className="flex flex-wrap gap-2 justify-between items-center">
                <div className="flex flex-wrap gap-2">
                  {tabs && tabs.map((tab) => (
                    <TabChip
                      key={tab.value}
                      label={tab.label}
                      count={tab.count}
                      active={activeTab === tab.value}
                      onClick={() => onTabChange?.(tab.value)}
                      color={tab.color as any}
                    />
                  ))}
                </div>
                <div className="flex items-center gap-2">
                  {/* 선택된 항목 수 표시 */}
                  {selectedItems.size > 0 && (
                    <span className="text-sm text-muted-foreground">
                      {selectedItems.size}개 항목 선택됨
                    </span>
                  )}
                  {/* 테이블 헤더 액션 (총 N건 등) - 필터 앞에 배치 */}
                  {tableHeaderActions}
                  {/* filterConfig 기반 자동 필터 (PC) */}
                  {renderAutoFilters()}
                  {selectedItems.size >= 1 && onBulkDelete && (
                    <Button
                      variant="outline"
                      size="sm"
                      onClick={handleBulkDeleteClick}
                      className="flex items-center gap-2 bg-gray-900 text-white hover:bg-gray-800 hover:text-white"
                    >
                      <Trash2 className="h-4 w-4" />
                      선택 삭제({selectedItems.size})
                    </Button>
                  )}
                </div>
              </div>
            </div>

            {/* 모바일/태블릿 (~1279px) - TabChip 탭 */}
            {tabs && tabs.length > 0 && (
              <div className="xl:hidden mb-4 overflow-x-auto">
                <div className="flex gap-2 min-w-max">
                  {tabs.map((tab) => (
                    <TabChip
                      key={tab.value}
                      label={tab.label}
                      count={tab.count}
                      active={activeTab === tab.value}
                      onClick={() => onTabChange?.(tab.value)}
                      color={tab.color as any}
                    />
                  ))}
                </div>
              </div>
            )}

            {/* 탭 컨텐츠 */}
            {(tabs || [{ value: 'default', label: '', count: 0 }]).map((tab) => (
              <TabsContent key={tab.value} value={tab.value} className="mt-0">
                {/* 모바일/태블릿/소형 노트북 (~1279px) - 선택 삭제 버튼 */}
                {selectedItems.size >= 2 && onBulkDelete && (
                  <div className="xl:hidden fixed bottom-0 left-0 right-0 p-4 bg-white border-t shadow-lg z-50">
                    <Button
                      variant="destructive"
                      size="lg"
                      onClick={handleBulkDeleteClick}
                      className="w-full flex items-center justify-center gap-2"
                    >
                      <Trash2 className="h-4 w-4" />
                      선택 삭제 ({selectedItems.size})
                    </Button>
                  </div>
                )}

                {/* 모바일/카드 뷰용 필터 - filterConfig 자동 생성 또는 기존 mobileFilterSlot */}
                {(filterConfig || mobileFilterSlot) && (
                  <div className="xl:hidden mb-4">
                    {/* filterConfig가 있으면 자동 생성된 MobileFilter 사용 */}
                    {renderAutoMobileFilter()}
                    {/* 기존 방식: mobileFilterSlot 직접 전달 */}
                    {mobileFilterSlot}
                  </div>
                )}

                {/* 모바일/태블릿/소형 노트북 (~1279px) 카드 뷰 */}
                <div className="xl:hidden space-y-4 md:space-y-0 md:grid md:grid-cols-2 md:gap-4 lg:grid-cols-3">
                  {isLoading ? (
                    <div className="col-span-full">
                      <MobileCardGridSkeleton count={6} showCheckbox={showCheckbox} />
                    </div>
                  ) : mobileData.length === 0 ? (
                    <div className="text-center py-6 text-muted-foreground border rounded-lg text-[14px]">
                      검색 결과가 없습니다.
                    </div>
                  ) : (
                    // 인피니티 스크롤: mobileData는 allData?.slice(0, displayCount) || data
                    mobileData.map((item, index) => {
                      const itemId = getItemId(item);
                      const isSelected = selectedItems.has(itemId);
                      // 순차 번호: 1번부터 시작
                      const globalIndex = index + 1;

                      return (
                        <div key={itemId}>
                          {renderMobileCard(
                            item,
                            index,
                            globalIndex,
                            isSelected,
                            () => onToggleSelection(itemId)
                          )}
                        </div>
                      );
                    })
                  )}
                  {/* 모바일 인피니티 스크롤 - 더 보기 버튼 & 로딩 표시 */}
                  {mobileData.length > 0 && (
                    <div className="col-span-full">
                      {hasMoreData ? (
                        <>
                          {/* 스크롤 감지용 Sentinel */}
                          <div
                            ref={mobileScrollSentinelRef}
                            className="h-4 w-full"
                            aria-hidden="true"
                          />
                          {/* 더 보기 버튼 + 진행 상황 */}
                          <div className="flex flex-col items-center gap-2 py-4">
                            {isMobileLoading ? (
                              <div className="flex items-center gap-2 text-sm text-muted-foreground">
                                <Loader2 className="h-4 w-4 animate-spin" />
                                불러오는 중...
                              </div>
                            ) : (
                              <Button
                                variant="outline"
                                size="sm"
                                onClick={isServerSidePagination ? handleLoadMoreMobile : handleLoadMoreClient}
                                className="w-full max-w-xs"
                              >
                                더 보기
                              </Button>
                            )}
                            <span className="text-xs text-muted-foreground">
                              {loadedCount.toLocaleString()} / {totalDataCount.toLocaleString()}
                            </span>
                          </div>
                        </>
                      ) : (
                        <div className="text-center py-4 text-sm text-muted-foreground">
                          모든 항목을 불러왔습니다 ({totalDataCount.toLocaleString()}개)
                        </div>
                      )}
                    </div>
                  )}
                </div>

                {/* 데스크톱 (1280px+) 테이블 뷰 */}
                <div className="hidden xl:block rounded-md border overflow-x-auto [&::-webkit-scrollbar]:h-3 [&::-webkit-scrollbar-track]:bg-gray-100 [&::-webkit-scrollbar-thumb]:bg-gray-300 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:hover:bg-gray-400" style={{ scrollbarWidth: 'thin', scrollbarColor: '#d1d5db #f3f4f6' }}>
                  {isLoading ? (
                    <TableSkeleton
                      rows={pagination.itemsPerPage || 10}
                      columns={tableColumns.length}
                      showCheckbox={showCheckbox}
                      showActions={tableColumns.some(col => col.key === 'actions')}
                    />
                  ) : (
                  <Table>
                    <TableHeader>
                      <TableRow>
                        {renderCustomTableHeader ? (
                          // 커스텀 테이블 헤더 사용 (동적 컬럼용)
                          renderCustomTableHeader()
                        ) : (
                          // 기본 테이블 헤더
                          <>
                            {showCheckbox && (
                              <TableHead className="w-[50px] min-w-[50px] max-w-[50px] text-center">
                                <Checkbox
                                  checked={allSelected}
                                  onCheckedChange={onToggleSelectAll}
                                />
                              </TableHead>
                            )}
                            {tableColumns.map((column) => {
                              // "actions" 컬럼은 항상 렌더링하되, 선택된 항목이 없을 때는 빈 헤더로 표시
                              const isSortable = column.sortable && onSort;
                              const isCurrentSort = sortBy === column.key;

                              return (
                                <TableHead
                                  key={column.key}
                                  className={`${column.className || ''} ${isSortable ? 'cursor-pointer select-none hover:bg-muted/50' : ''}`}
                                  onClick={isSortable ? () => onSort(column.key) : undefined}
                                >
                                  {column.key === "actions" && selectedItems.size === 0 ? "" : (
                                    <div className={`flex items-center gap-1 ${isSortable ? 'group' : ''}`}>
                                      <span>{column.label}</span>
                                      {isSortable && (
                                        <span className="text-muted-foreground">
                                          {isCurrentSort ? (
                                            sortOrder === 'asc' ? (
                                              <ArrowUp className="h-4 w-4" />
                                            ) : (
                                              <ArrowDown className="h-4 w-4" />
                                            )
                                          ) : (
                                            <ArrowUpDown className="h-4 w-4 opacity-0 group-hover:opacity-50" />
                                          )}
                                        </span>
                                      )}
                                    </div>
                                  )}
                                </TableHead>
                              );
                            })}
                          </>
                        )}
                      </TableRow>
                    </TableHeader>
                    <TableBody className="[&_tr]:h-14 [&_tr]:min-h-[56px] [&_tr]:max-h-[56px]">
                      {data.length === 0 ? (
                        <TableRow>
                          <TableCell
                            colSpan={tableColumns.length + (showCheckbox ? 1 : 0)}
                            className="h-24 text-center"
                          >
                            검색 결과가 없습니다.
                          </TableCell>
                        </TableRow>
                      ) : (
                        // 백엔드가 created_at ASC로 정렬해서 보내줌 (오래된 순)
                        data.map((item, index) => {
                          const itemId = getItemId(item);
                          // 순차 번호: startIndex 기준으로 1부터 시작
                          const globalIndex = startIndex + index + 1;
                          return (
                            <Fragment key={itemId}>
                              {renderTableRow(item, index, globalIndex)}
                            </Fragment>
                          );
                        })
                      )}
                    </TableBody>
                    {tableFooter && (
                      <TableFooter>
                        {tableFooter}
                      </TableFooter>
                    )}
                  </Table>
                  )}
                </div>
              </TabsContent>
            ))}
          </Tabs>
        </CardContent>
      </Card>

      {/* 페이지네이션 - 데스크톱에서만 표시 (1페이지여도 항상 표시) */}
      <div className="hidden xl:flex items-center justify-between">
        <div className="text-sm text-muted-foreground">
          전체 {pagination.totalItems}개 중 {pagination.totalItems > 0 ? startIndex + 1 : 0}-{Math.min(startIndex + pagination.itemsPerPage, pagination.totalItems)}개 표시
        </div>
        <div className="flex items-center gap-2">
          <Button
            variant="outline"
            size="sm"
            onClick={() => pagination.onPageChange(pagination.currentPage - 1)}
            disabled={pagination.currentPage === 1}
          >
            이전
          </Button>
          <div className="flex items-center gap-1">
            {Array.from({ length: pagination.totalPages }, (_, i) => i + 1).map((page) => {
              // 현재 페이지 근처만 표시
              if (
                page === 1 ||
                page === pagination.totalPages ||
                (page >= pagination.currentPage - 2 && page <= pagination.currentPage + 2)
              ) {
                return (
                  <Button
                    key={page}
                    variant={page === pagination.currentPage ? "default" : "outline"}
                    size="sm"
                    onClick={() => pagination.onPageChange(page)}
                    className="min-w-[36px]"
                  >
                    {page}
                  </Button>
                );
              } else if (
                page === pagination.currentPage - 3 ||
                page === pagination.currentPage + 3
              ) {
                return <span key={page} className="px-2">...</span>;
              }
              return null;
            })}
          </div>
          <Button
            variant="outline"
            size="sm"
            onClick={() => pagination.onPageChange(pagination.currentPage + 1)}
            disabled={pagination.currentPage === pagination.totalPages}
          >
            다음
          </Button>
        </div>
      </div>

      {/* 일괄 삭제 확인 다이얼로그 */}
      <DeleteConfirmDialog
        open={showDeleteDialog}
        onOpenChange={setShowDeleteDialog}
        onConfirm={handleConfirmDelete}
        description={
          <>
            선택한 <strong>{selectedItems.size}개</strong>의 항목을 삭제하시겠습니까?
            <br />
            <span className="text-muted-foreground text-sm">
              삭제된 항목은 복구할 수 없습니다. 관련된 데이터도 함께 삭제될 수 있습니다.
            </span>
          </>
        }
      />
    </PageLayout>
  );
}

// 필터 관련 타입 재export (다른 페이지에서 사용 가능)
export type { FilterFieldConfig, FilterValues } from "@/components/molecules/MobileFilter";