import { ProTable, ProTableProps } from '@ant-design/pro-components';
import type { ParamsType } from '@ant-design/pro-provider';
import { Modal, ModalProps, type TableProps } from 'antd';
import React, {
  forwardRef,
  useEffect,
  useImperativeHandle,
  useState,
} from 'react';

export interface ISelectModalProps<
  DataType extends Record<string, any>,
  Params extends ParamsType = ParamsType,
> {
  rowKey: string;
  type: 'checkbox' | 'radio' | undefined;
  onFinish: (selectId: DataType[]) => void;
  num?: number;
  selectedList?: DataType[];
  modalProps: ModalProps;
  tableProps: ProTableProps<DataType, Params>;
  ghost?: boolean;
  disabled?: (record: DataType) => boolean;
}

export type SelectModalHandle<DataType> = {
  onFinish: () => void;
  getSelectedList: () => DataType[];
};

const SelectModal = forwardRef<
  SelectModalHandle<any>,
  ISelectModalProps<any, any>
>(
  <
    DataType extends Record<string, any>,
    Params extends ParamsType = ParamsType,
  >(
    props: ISelectModalProps<DataType, Params>,
    ref: React.Ref<SelectModalHandle<DataType>>,
  ) => {
    const {
      rowKey,
      type: selectType,
      num,
      onFinish,
      modalProps,
      tableProps,
      ghost = false,
      disabled,
    } = props;

    const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
    const [selectedList, setSelectedList] = useState<DataType[]>([]);
    const [open, setOpen] = useState<boolean>();

    useEffect(() => {
      setOpen(modalProps?.open);
    }, [modalProps?.open]);

    useImperativeHandle(ref, () => ({
      getSelectedList: () => selectedList,
      onFinish: () => {
        onFinish(selectedList);
        if (!selectedList || selectedList.length === 0) {
          return;
        }
        setSelectedRowKeys([]);
        setSelectedList([]);
        setOpen(false);
      },
    }));

    useEffect(() => {
      const initSelectedList = props.selectedList;
      if (initSelectedList) {
        setSelectedRowKeys(
          initSelectedList?.map((v) => {
            return v?.[rowKey] || '';
          }),
        );
        setSelectedList(initSelectedList);
      } else {
        setSelectedRowKeys([]);
        setSelectedList([]);
      }
    }, [props.selectedList]);

    const rowSelection: TableProps<DataType>['rowSelection'] = {
      selectedRowKeys,
      preserveSelectedRowKeys: true,
      type: selectType || 'checkbox',
      onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
        const dataTypes = [...selectedList, ...selectedRows];
        const newSelectList = selectedRowKeys
          .map((v) =>
            dataTypes.find((item: DataType) => {
              return item?.[rowKey] === v;
            }),
          )
          .filter((item) => item !== undefined);
        setSelectedRowKeys(selectedRowKeys.slice(0, num || 1));
        setSelectedList(newSelectList.slice(0, num || 1) as any);
      },
      getCheckboxProps: (record: DataType) => ({
        disabled:
          ((selectType || 'checkbox') === 'checkbox' &&
            selectedList.length >= (num || 1) &&
            !selectedRowKeys.includes(record?.[rowKey] || '')) ||
          disabled?.(record),
      }),
    };

    const renderTable = () => {
      return (
        <ProTable<DataType, Params>
          ghost={true}
          search={false}
          scroll={{ x: 'max-content' }}
          onRow={(data) => ({
            onClick: () => {
              if (disabled?.(data)) {
                return;
              }
              if (selectType === 'radio') {
                setSelectedRowKeys([data[rowKey]]);
                setSelectedList([data]);
              }

              if (selectType === 'checkbox') {
                if (selectedList.length >= (num || 1)) {
                  return;
                }
                if (selectedRowKeys.length === 0) {
                  setSelectedRowKeys([data[rowKey]]);
                  setSelectedList([data]);
                } else {
                  if (selectedRowKeys.includes(data[rowKey])) {
                    setSelectedRowKeys(
                      selectedRowKeys
                        .filter((v) => v !== data[rowKey])
                        .slice(0, num || 1),
                    );
                    setSelectedList(
                      selectedList
                        .filter((v) => v?.[rowKey] !== data[rowKey])
                        .slice(0, num || 1),
                    );
                  } else {
                    setSelectedRowKeys(
                      [...selectedRowKeys, data[rowKey]].slice(0, num || 1),
                    );
                    setSelectedList([...selectedList, data].slice(0, num || 1));
                  }
                }
              }

              if (ghost) {
                onFinish([data]);
              }
            },
          })}
          rowSelection={rowSelection}
          {...tableProps}
        />
      );
    };

    if (ghost) {
      return renderTable();
    }

    return (
      <Modal
        width={1000}
        {...modalProps}
        open={open}
        onOk={async (event) => {
          onFinish(selectedList);

          if (!selectedList || selectedList.length === 0) {
            return;
          }
          modalProps.onOk?.(event);
          if (selectedList && !modalProps.open) {
            setSelectedRowKeys([]);
            setSelectedList([]);
          }
        }}
      >
        {renderTable()}
      </Modal>
    );
  },
) as <
  DataType extends Record<string, any>,
  Params extends ParamsType = ParamsType,
>(
  props: ISelectModalProps<DataType, Params> & React.RefAttributes<unknown>,
) => JSX.Element;

export default SelectModal;
