import React, { useMemo, useState } from 'react';
import usePagination, { Offset } from 'src/hooks/usePagination';
import {
  SelectFilter,
  NumberInput,
  resolveFilterByType,
  type Filter,
} from './Filters';

export type DataItem = Record<string, any>;

export enum HeaderType {
  Index,
  String,
  Number,
  Select,
  Multiple,
}

export interface Header {
  key: string;
  header: string;
  formatter?: (data: DataItem) => string;
  type: HeaderType;
}

interface Props {
  data: DataItem[];
  headers: Header[];
}

export default function Table({ data, headers }: Props): JSX.Element {
  const [filters, setFilters] = useState<Record<string, Filter>>({});

  const filteredItems = useMemo(() => {
    return data.filter((item) => {
      return Object.entries(filters).every(([key, filter]) =>
        resolveFilterByType(filter, item[key]),
      );
    });
  }, [data, filters]);

  const { items, changeOffset } = usePagination<DataItem>({
    items: filteredItems,
  });

  const handleUpdateFilters = (
    name: string,
    type: HeaderType,
    value: any,
  ): void => {
    console.log(value);
    setFilters((prev) => {
      if (value === null) {
        // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
        delete prev[name];
        return { ...prev };
      }

      return { ...prev, [name]: { value, type } };
    });
  };

  function formatCell(data: DataItem, header: Header): JSX.Element {
    // This formatting is only used because the source is trusted (private markdown files manage only by me)
    // and because Astro don't allow me to pass JSX from an Astro file to a TSX file,
    // so I have to pass the formatted row as a string.
    // DON'T use this method on a public API
    if (header.formatter != null) {
      return (
        <div dangerouslySetInnerHTML={{ __html: header.formatter(data) }} />
      );
    }

    if (header.type === HeaderType.Multiple) {
      return (
        <ul className="text-start">
          {data[header.key].map((item: JSX.Element, idx: number) => (
            <li key={idx}>{item}</li>
          ))}
        </ul>
      );
    }

    return data[header.key];
  }

  function formatFilter(header: Header): JSX.Element {
    switch (header.type) {
      case HeaderType.String:
        return (
          <input
            onChange={(e) => {
              handleUpdateFilters(
                header.key,
                header.type,
                e.target.value ?? null,
              );
            }}
          />
        );
      case HeaderType.Number:
        return (
          <NumberInput
            keyData={header.key}
            onChange={(value) => {
              handleUpdateFilters(header.key, header.type, value);
            }}
          />
        );
      case HeaderType.Select:
        return (
          <SelectFilter
            data={data}
            keyData={header.key}
            onChange={(value) => {
              handleUpdateFilters(header.key, header.type, value);
            }}
          />
        );
      case HeaderType.Multiple:
        return (
          <SelectFilter
            isMultiple
            data={data}
            keyData={header.key}
            onChange={(value) => {
              handleUpdateFilters(header.key, header.type, value);
            }}
          />
        );

      default:
        break;
    }

    return <></>;
  }

  return (
    <>
      <table style={{ width: '100%' }}>
        <thead>
          <tr>
            {headers.map((item, idx) => (
              <th key={idx}>
                <div className="vstack">
                  {item.header}
                  {formatFilter(item)}
                </div>
              </th>
            ))}
          </tr>
        </thead>

        <tbody>
          {items.map((item, idx) => (
            <tr key={idx}>
              {headers.map((header, hidx) => (
                <td key={hidx}>{formatCell(item, header)}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>

      <section>
        <button
          onClick={() => {
            changeOffset(Offset.First);
          }}
        >
          First
        </button>
        <button
          onClick={() => {
            changeOffset(Offset.Prev);
          }}
        >
          Prev
        </button>
        <button
          onClick={() => {
            changeOffset(Offset.Next);
          }}
        >
          Next
        </button>
        <button
          onClick={() => {
            changeOffset(Offset.Last);
          }}
        >
          Last
        </button>
      </section>
    </>
  );
}
