import type {
  ColumnDef,
  SortingState,
  RowData,
  Row as RowType,
  PaginationState,
} from '@tanstack/react-table';
import type { CSSProperties } from 'react';
import {
  flexRender,
  getCoreRowModel,
  useReactTable,
  getSortedRowModel,
  createColumnHelper,
  getPaginationRowModel,
} from '@tanstack/react-table';
import React, { useCallback, useMemo, useState } from 'react';
import { Flex, Pagination, Spinner, Typo } from '../../atoms';
import { TriangleArrowDownIcon } from '../../icons/down';
import { TriangleArrowUpIcon } from '../../icons/up';
import { Row, type ExpandComp, type OnRowClick } from './row';

export type { ExpandComp, OnRowClick, ColumnDef, RowType as Row };
export { createColumnHelper };

export interface ExpandTableProps<Datum extends RowData> {
  /** 렌더할 데이터 배열  */
  data?: Datum[];
  /** 테이블 컬럼의 메타 데이터 */
  columns: ColumnDef<Datum>[];
  /** Row의 open이 true인 경우 하단의 collapse에 렌더할 컴포넌트 */
  ExpandComp?: ExpandComp<Datum>;
  /** 데이테가 없을 시 화면에 표시할 컴포넌트 */
  placeholder?: React.ReactNode;
  /** 행 클릭 시 실행할 콜백 */
  onRowClick?: OnRowClick<Datum>;
  getRowStyle?: (data: Datum) => CSSProperties;
  getCellStyle?: (data: Datum) => CSSProperties;
  tableStyle?: CSSProperties;
  isLoading?: boolean;

  /** pagination - 표시 유무, 기본값 true */
  showPagination?: boolean;
  /** pagination - 현재 선택된 페이지, 0부터 시작 */
  pageIndex?: number;
  /** pagination - 한 페이지에 표시될 컬럼 개수, 기본값 10 */
  pageSize?: number;
  /** pagination -  */
  siblingCount?: number;
}

export function ExpandTable<Datum extends RowData>(
  props: ExpandTableProps<Datum>
): React.ReactNode {
  const {
    data,
    columns,
    ExpandComp,
    placeholder,
    onRowClick,
    tableStyle,
    showPagination = true,
    isLoading,
    pageIndex = 0,
    pageSize = 10,
    siblingCount = 2,
    getRowStyle,
    getCellStyle,
  } = props;
  const defaultAlign = 'left';
  const [pagination, setPagination] = useState<PaginationState>({
    pageIndex: pageIndex || 0,
    pageSize: pageSize || 9999,
  });

  const [sorting, setSorting] = useState<SortingState>([]);
  const { getRowModel, getHeaderGroups, setPageIndex } = useReactTable({
    data: data || [],
    columns,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    state: {
      sorting,
      pagination,
    },
    onSortingChange: setSorting,
    getPaginationRowModel: getPaginationRowModel(),
    onPaginationChange: setPagination,
  });

  const gridTemplateColumns = useMemo<string>(() => {
    return columns
      .map((col) => {
        if (col.meta?.autoSize) return `minmax(${col.size || 50}px, 1fr)`;
        return `${col.size || 150}px`;
      })
      .join(' ');
  }, [columns]);

  const rowModels = getRowModel();

  const onChangePagination = useCallback(
    (page: number) => {
      setPageIndex(page - 1);
    },
    [setPageIndex]
  );

  const helpCompRender = useCallback(
    (rowLength: number) => {
      if (isLoading) {
        return (
          <Flex
            align="center"
            height="100%"
            justify="center"
            p="5"
            width="100%"
          >
            <Spinner />
          </Flex>
        );
      }
      if (rowLength === 0) {
        return (
          <div className="tr" key="expand_placeholder">
            <Flex align="center" justify="center">
              {placeholder || (
                <Typo color="gray" mb="6" mt="6" variant="body">
                  데이터가 없습니다
                </Typo>
              )}
            </Flex>
          </div>
        );
      }
      return null;
    },
    [isLoading, placeholder]
  );

  const pageCount = useMemo(() => {
    if (!data) return 0;
    return Math.ceil(data.length / pageSize);
  }, [data, pageSize]);

  return (
    <div className="expand-table-wrapper">
    <div className="expand-table" style={tableStyle}>
      <div className="thead">
        {getHeaderGroups().map((headerGroup) => (
          <div
            className="tr"
            key={headerGroup.id}
            style={{ gridTemplateColumns }}
          >
            {headerGroup.headers.map((header) => {
              const sortable = header.column.getCanSort();
              const sortedState = header.column.getIsSorted();
              const justifyContent =
                header.column.columnDef.meta?.align || defaultAlign;

              return (
                <div className={`${justifyContent} th`} key={header.id}>
                  <button
                    onClick={header.column.getToggleSortingHandler()}
                    style={
                      sortable ? { cursor: 'pointer' } : { cursor: 'default' }
                    }
                    type="button"
                  >
                    <Typo as="div" variant="body">
                      {flexRender(
                        header.column.columnDef.header,
                        header.getContext()
                      )}
                    </Typo>
                    {sortable ? (
                      <Flex
                        direction="column"
                        style={{ marginLeft: 'var(--space-2)' }}
                      >
                        <TriangleArrowUpIcon
                          color={
                            sortedState === 'asc'
                              ? 'var(--iris-10)'
                              : 'var(--iris-6)'
                          }
                        />
                        <TriangleArrowDownIcon
                          color={
                            sortedState === 'desc'
                              ? 'var(--iris-10)'
                              : 'var(--iris-6)'
                          }
                        />
                      </Flex>
                    ) : null}
                  </button>
                </div>
              );
            })}
          </div>
        ))}
      </div>
      <div className="tbody">
        {/* 조건에 따라 placeholder 또는 loading 렌더*/}
        {helpCompRender(rowModels.rows.length) ||
          rowModels.rows.map((row) => {
            return (
              <Row
                ExpandComp={ExpandComp}
                defaultAlign={defaultAlign}
                getCellStyle={getCellStyle}
                getRowStyle={getRowStyle}
                gridTemplateColumns={gridTemplateColumns}
                key={`row_${row.id}`}
                onRowClick={onRowClick}
                row={row}
              />
            );
          })}
      </div>
    </div>
      {showPagination ? (
          <Flex justify="end" pt="3" width="100%">
            <Pagination
              count={pageCount}
              onChange={onChangePagination}
              page={pagination.pageIndex + 1}
              siblingCount={siblingCount}
            />
          </Flex>
        ) : null}
    </div>
  );
}
