import React, { useContext, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { Table, Input, Row, Col, Spin, Popover, Alert, message, Segmented, Typography } from 'antd';
import { RightSquareFilled } from '@ant-design/icons';
// @ts-ignore TODO: fix types for lodash
import debounce from 'lodash.debounce';
import PageContext from '../../contexts/page';
import { getColumnRender } from '../../ddRender/ddRender';
import { findDuplicates, getFilteredData, getFilteredSortedData, getInitialFilter, updateUrl, getColumnSortOrder, getInitialSorter, filterCols } from './helpers';
import RefTableLinks from '../RefTableLinks';
import * as constants from '../../constants';
import ImageCardTable from './ImageCardTable';
import RandomList from '../RandomList';

// Use `Typography` so can apply dark theme to text
import { jsxs as _jsxs } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
const {
  Text
} = Typography;
const defaultPage = 1;
const defaultPageSize = 10;
const debouncedUpdateUrl = debounce(updateUrl, 500);
const TableView = 'table_view';
const ImageView = 'image_view';
const RandomView = 'random_view';

/**
 * URL params:
 * - page: number
 * - pageSize: number
 * - filter: string
 * - sorter: string
 * - view: string (table_view or image_view)
 */
const ListPage = props => {
  const {
    columns,
    tableName,
    primaryKey,
    dbName,
    githubDb
  } = useContext(PageContext);

  /**
   * @example filter = { url: 'https://example.com', name: 'John' }
   */
  const [filter, setFilter] = useState({}); // getInitialFilter(filterCols()), cannot get context in constructor
  const [sorter, setSorter] = useState({
    columnKey: '',
    // e.g. "url"
    order: '' //  "ascend" or "descend" or undefined
  });

  const [loading, setLoading] = useState('');
  const [errMsg, setErrMsg] = useState('');
  const [rows, setRows] = useState(null);
  const [contentTableName, setContentTableName] = useState(''); // the current table name of data this.state.rows
  // TODO to improve this part, how to get initial value from URL?
  const [page, setPage] = useState(() => {
    return Number(new URL(window.location.href).searchParams.get('page')) || defaultPage;
  });
  const [pageSize, setPageSize] = useState(() => {
    return Number(new URL(window.location.href).searchParams.get('pageSize')) || defaultPageSize;
  });
  const [view, setView] = useState(() => {
    return new URL(window.location.href).searchParams.get('view') || TableView;
  });
  const controllerRef = useRef(new AbortController());
  useEffect(() => {
    getData(tableName);
    setFilter(getInitialFilter(filterCols(columns)));
    setSorter(getInitialSorter());
    return () => {
      // When your component unmounts

      // Cancel all HTTP requests in this page
      controllerRef.current.abort();
    };
  }, []);
  const filteredSortedData = getFilteredSortedData(columns, filter, sorter, rows);
  const handleKeyDown = e => {
    // Check if the focus is on an input element
    if (document.activeElement?.tagName === 'INPUT') {
      e.stopPropagation();
      return;
    }
    if (e.key === 'ArrowRight') {
      setPage(prevPage => {
        if (prevPage < filteredSortedData.length / pageSize) {
          window.scrollTo({
            top: 0,
            behavior: 'smooth'
          });
          debouncedUpdateUrl({
            page: prevPage + 1
          });
          return prevPage + 1;
        } else {
          message.info('This is the last page!');
          return prevPage;
        }
      });
    } else if (e.key === 'ArrowLeft') {
      setPage(prevPage => {
        if (prevPage > 1) {
          window.scrollTo({
            top: 0,
            behavior: 'smooth'
          });
          debouncedUpdateUrl({
            page: prevPage - 1
          });
          return prevPage - 1;
        } else {
          message.info('This is the first page!');
          return prevPage;
        }
      });
    }
  };
  useEffect(() => {
    window.addEventListener('keydown', handleKeyDown, false);
    return () => {
      window.removeEventListener('keydown', handleKeyDown, false);
    };
  }, [rows]);
  useEffect(() => {
    getData(props.tableName);
  }, [props.tableName]);
  const handleFilterChange = key => event => {
    setFilter(prevFilter => {
      const newFilter = {
        ...prevFilter,
        [key]: event.target.value
      };
      debouncedUpdateUrl({
        filter: newFilter
      });
      return newFilter;
    });
  };
  const handleTableChange = (pagination, filters, newSorter /* , extra */) => {
    setPage(pagination.current || defaultPage);
    setPageSize(pagination.pageSize || defaultPageSize);
    setSorter({
      // @ts-ignore TODO: fix types for antd
      columnKey: newSorter.columnKey,
      // @ts-ignore TODO: fix types for antd
      order: newSorter.order // order could be undefined
    });

    debouncedUpdateUrl({
      page: pagination.current,
      pageSize: pagination.pageSize,
      sorter: {
        // @ts-ignore TODO: fix types for antd
        columnKey: newSorter.columnKey,
        // @ts-ignore TODO: fix types for antd
        order: newSorter.order // order could be undefined
      }
    });

    window.scrollTo({
      top: 0,
      behavior: 'smooth'
    });
  };
  const handleCardTableChange = pagination => {
    setPage(pagination.current || defaultPage);
    setPageSize(pagination.pageSize || defaultPageSize);
    debouncedUpdateUrl({
      page: pagination.current,
      pageSize: pagination.pageSize
    });
    window.scrollTo({
      top: 0,
      behavior: 'smooth'
    });
  };
  const getData = async tableName => {
    setLoading(`Loading ${dbName}/${tableName} ...`);
    try {
      const {
        content
      } = await githubDb.getTableRows(dbName, tableName, controllerRef.current.signal);
      setRows(content);
      setContentTableName(tableName);
    } catch (error) {
      console.error('Failed to get JSON file in List component, error:', error); // eslint-disable-line no-console
      setErrMsg(`Failed to get data: ${error.message}`);
    }
    setLoading('');
  };
  const alertDuplicatedRowKey = () => {
    if (!rows) return null;
    const duplicatedRowKeys = findDuplicates(rows.map(item => item[primaryKey]));
    if (duplicatedRowKeys.length === 0) return null;
    return /*#__PURE__*/_jsxs("div", {
      style: {
        color: 'red'
      },
      children: ["Duplicated row keys(", duplicatedRowKeys.length, "): ", duplicatedRowKeys.join(', ')]
    });
  };
  const alertTableDataInvalid = () => {
    const invalidRows = [];
    if (!rows) return null;
    rows.forEach((row, idx) => {
      if (row[primaryKey] === undefined || row[primaryKey] === null) {
        invalidRows.push({
          rowIdx: idx,
          rowData: row
        });
      }
    });
    if (invalidRows.length === 0) return null;
    return /*#__PURE__*/_jsxs("div", {
      style: {
        color: 'red'
      },
      children: ["Invalid rows(", invalidRows.length, "):", ' ', invalidRows.map(row => /*#__PURE__*/_jsxs("div", {
        children: [`idx:${row.rowIdx}`, " ", JSON.stringify(row.rowData)]
      }))]
    });
  };
  const alertValidations = () => /*#__PURE__*/_jsxs("div", {
    className: "dbm-alert-validations",
    children: [alertDuplicatedRowKey(), alertTableDataInvalid()]
  });
  const getTableColumns = () => {
    const cols = columns.filter(column => column[constants.TYPE_LIST_PAGE] !== 'HIDE').map(column => {
      // Table component of antd
      const antdCol = {
        key: column.id,
        title: column.name,
        dataIndex: column.id,
        // Order of sorted values: 'ascend', 'descend', false
        sortOrder: getColumnSortOrder(column.id, sorter),
        sorter: true,
        ...column.tableProps
      };
      const renderFn = getColumnRender(constants.TYPE_LIST_PAGE, column);
      if (renderFn) {
        antdCol.render = renderFn;
      }
      if (column.referenceTable) {
        const lastRender = antdCol.render || (val => val);
        const hasVal = val => {
          if (column.type === constants.STRING_ARRAY) {
            if (!val || val.length === 0) return false;
            return true;
          }
          return !!val;
        };
        // If this column has ref table, then render links to ref table item
        antdCol.render = (...args) => /*#__PURE__*/_jsxs("div", {
          children: [lastRender(...args), ' ', hasVal(args[0]) && /*#__PURE__*/_jsx(Popover, {
            title: "Ref Table Links",
            trigger: "click",
            content: /*#__PURE__*/_jsx(RefTableLinks, {
              value: args[0],
              column: column
            }),
            children: /*#__PURE__*/_jsx(RightSquareFilled, {})
          })]
        });
      }
      return antdCol;
    });

    // common columns
    cols.push({
      key: 'createdAt',
      title: 'createdAt',
      dataIndex: 'createdAt',
      sortOrder: getColumnSortOrder('createdAt', sorter),
      sorter: true
    });
    cols.push({
      key: 'updatedAt',
      title: 'updatedAt',
      dataIndex: 'updatedAt',
      sortOrder: getColumnSortOrder('updatedAt', sorter),
      sorter: true
    });
    cols.push({
      key: 'actions',
      dataIndex: primaryKey,
      title: 'Actions',
      render: id => {
        // id is the primary key of the row, for sample case the primary key value is 'https://example.com?id=1&name=John'
        // so need to encodeURI to make it as a valid URL
        const search = `?${primaryKey}=${encodeURIComponent(id)}`;
        return /*#__PURE__*/_jsxs("div", {
          children: [/*#__PURE__*/_jsx(Link, {
            to: {
              pathname: `/${dbName}/${tableName}/update`,
              search
            },
            children: "Update"
          }), ' ', "|", ' ', /*#__PURE__*/_jsx(Link, {
            to: {
              pathname: `/${dbName}/${tableName}/get`,
              search
            },
            children: "Detail"
          })]
        });
      }
    });
    return cols;
  };
  const renderTable = () => {
    // When router changed, before loading next table rows,
    // contentTableName is old table, but this.props.tableName is new table.
    if (contentTableName !== tableName) return null;
    return /*#__PURE__*/_jsxs("div", {
      children: [alertValidations(), /*#__PURE__*/_jsx(Table, {
        size: "small",
        showSorterTooltip: false,
        rowKey: primaryKey,
        columns: getTableColumns(),
        dataSource: filteredSortedData,
        pagination: {
          current: page,
          pageSize,
          // total: filteredSortedData.length,
          showQuickJumper: true,
          showTotal: total => `Total ${total} items`
        },
        onChange: handleTableChange
      })]
    });
  };
  const renderTableViewOrImageView = () => {
    if (loading) return /*#__PURE__*/_jsx(Spin, {
      tip: loading,
      children: "Loading..."
    });
    if (errMsg) return /*#__PURE__*/_jsx(Alert, {
      message: errMsg,
      type: "error"
    });
    if (!rows) return null;
    switch (view) {
      case TableView:
        return renderTable();
      case ImageView:
        return /*#__PURE__*/_jsx(ImageCardTable, {
          imgKey: columns.find(col => col['ui:listPage:isImageViewKey'])?.id,
          dataSource: filteredSortedData,
          pagination: {
            current: page,
            pageSize
          },
          onChange: handleCardTableChange
        });
      case RandomView:
        return /*#__PURE__*/_jsx(RandomList, {
          rows: getFilteredData(columns, filter, rows)
        });
      default:
        return null;
    }
  };
  return /*#__PURE__*/_jsxs("div", {
    className: "dbm-list-page list-component",
    children: [/*#__PURE__*/_jsx("div", {
      className: "dbm-table-filter",
      children: /*#__PURE__*/_jsx(Row, {
        gutter: 10,
        children: [...filterCols(columns), {
          id: 'createdAt',
          name: 'createdAt'
        }, {
          id: 'updatedAt',
          name: 'updatedAt'
        }].map(f => /*#__PURE__*/_jsxs(Col, {
          span: 6,
          children: [/*#__PURE__*/_jsx(Text, {
            children: f.name
          }), ":", /*#__PURE__*/_jsx(Input, {
            size: "small",
            value: filter[f.id],
            onChange: handleFilterChange(f.id)
          })]
        }, f.id))
      })
    }), /*#__PURE__*/_jsx(Segmented, {
      value: view,
      options: [{
        label: 'Table View',
        value: TableView
      }, {
        label: 'Image View',
        value: ImageView
      }, {
        label: 'Random View',
        value: RandomView
      }],
      onChange: val => {
        setView(val + '');
        debouncedUpdateUrl({
          view: val
        });
      }
    }), renderTableViewOrImageView()]
  });
};
export default ListPage;
ListPage.propTypes = {
  // Even tableName is now passing from context,
  // but we need to pass props.tableName to get new data from backend API
  tableName: PropTypes.string.isRequired
};
//# sourceMappingURL=index.js.map