/**
 * @see https://primereact.org/datatable/
 */

import { TableFilterTypes, TableSize } from "../types/enums";
import { modelProps, TableCellValueType } from "../types/props";
import { Column, ColumnBodyOptions, ColumnProps } from "primereact/column";
import { DataTable, DataTableValue } from "primereact/datatable";
import React, { ReactNode } from "react";
import { NotSetValue, ValueFalse, ValueTrue } from "../helpers/TablerHelpers";
import {
  AuditIconButon,
  DeleteIconButton,
  EditIconButton,
  ViewIconButton,
} from "./buttons/TableActionButtons";

interface CustomColumnField {
  value: TableCellValueType;
  label?: string;
}

interface CustomColumnProps {
  [key: string]: CustomColumnField;
}

interface GridViewInterface {
  models: modelProps[];
  actions?: string | false;
  actionList?: string[];
  actionId?: string;
  columns?: CustomColumnProps | undefined;
  striped?: boolean;
  filter?: boolean;
  filterDisplay?: TableFilterTypes | undefined;
  sortable?: boolean;
  styling?: object;
  header?: ReactNode | false;
  footer?: ReactNode | false;
  size?: TableSize | undefined;
  paginator?: boolean;
  rowsPerPage?: number;
  rowsPerPageOptions?: number[] | undefined;
  emptyMessage?: string;
}

const PrimeGridView: React.FC<GridViewInterface> = ({
  models,
  columns = undefined,
  actions = false,
  actionList = ["audit", "view", "edit", "delete"],
  actionId = "id",
  striped = true,
  filter = true,
  filterDisplay = TableFilterTypes.MENU,
  sortable = true,
  styling = { minWidth: "50rem" },
  header = false,
  footer = false,
  size = undefined,
  paginator = true,
  rowsPerPage = 25,
  rowsPerPageOptions = [25, 50, 75, 100],
  emptyMessage = "No data available",
}) => {
  let data: DataTableValue[];
  let drawColumns: ColumnProps[];

  if (models.length > 0) {
    const labels = models[0].labels;

    drawColumns = Object.keys(labels).map((field) => ({
      field,
      header: labels[field],
      body: false,
    })) as ColumnProps[];

    data = models.map((model) => {
      const fields = { ...model.fields };

      if (actions !== false) {
        const navId = fields[actionId] as string;
        fields["actionButtons"] = CheckAddActionColumn(navId);
      }

      return fields;
    });

    if (columns !== undefined) {
      drawColumns = Object.keys(columns).map((field) => {
        return {
          field,
          header: columns[field]?.label ?? labels[field],
          body: columns[field]?.value ?? false,
        };
      }) as ColumnProps[];
    }

    checkAddActionColumnHeading();
  } else {
    // Draw empty table
    data = drawColumns = [];
  }

  interface RowData {
    [key: string]: unknown;
  }

  const RenderCellField = (rowData: RowData, columnData: ColumnBodyOptions) => {
    const field = columnData.field;
    const model = models.find((item) => {
      // Ensure the correct row is used on large data sets by comparing either the id field or the guid field if they exist
      if (item.fields["id"] !== undefined && rowData["id"]) {
        return (
          item.fields["id"] === rowData["id"] &&
          item.fields[field] === rowData[field]
        );
      } else if (item.fields["guid"] !== undefined && rowData["guid"]) {
        return (
          item.fields["guid"] === rowData["guid"] &&
          item.fields[field] === rowData[field]
        );
      }
      return item.fields[field] === rowData[field];
    });

    let columnBody = drawColumns.find((item) => item.field === field)?.body;
    const fallback = rowData[field] as string;

    if (typeof columnBody === "function" && model !== undefined) {
      type bodyFunction = (model: modelProps) => string;
      const bodyFunction = columnBody as bodyFunction;
      columnBody = bodyFunction(model);
    }

    if (columnBody === undefined) {
      columnBody = fallback;
    }

    if (columnBody === null || columnBody === "") {
      return NotSetValue();
    }

    if (columnBody === true) {
      return ValueTrue();
    } else if (columnBody === false) {
      return ValueFalse();
    }

    return columnBody as string;
  };

  return (
    <DataTable
      value={data}
      stripedRows={striped}
      tableStyle={styling}
      header={header}
      footer={footer}
      size={size}
      paginator={paginator}
      rows={rowsPerPage}
      rowsPerPageOptions={rowsPerPageOptions}
      sortMode="multiple"
      removableSort
      filterDisplay={filterDisplay}
      emptyMessage={emptyMessage}
    >
      {drawColumns.map((column: ColumnProps, i) => {
        return (
          <Column
            key={`${column.field}-${i}`}
            field={column.field}
            sortable={column.field !== "actionButtons" ? sortable : false}
            header={
              column.field !== "actionButtons" ? column.header : undefined
            }
            filter={column.field !== "actionButtons" ? filter : false}
            body={column.body !== false ? RenderCellField : undefined}
            align={column.field === "actionButtons" ? "center" : undefined}
          />
        );
      })}
    </DataTable>
  );

  function checkAddActionColumnHeading() {
    if (actions !== false) {
      drawColumns.push({
        field: "actionButtons",
        header: "Actions",
        body: false,
      });
    }
  }

  function CheckAddActionColumn(navId: string) {
    return (
      <>
        {actionList.map((action, index) =>
          getActionButton(actions as string, action, navId, index),
        )}
      </>
    );
  }
};

const getActionButton = (
  baseUri: string,
  action: string,
  id: string | number,
  index: number | string,
) => {
  switch (action) {
    case "audit":
      return (
        <AuditIconButon
          key={`view-${id}-${index}`}
          navId={id}
          baseUri={baseUri}
        />
      );
    case "view":
      return (
        <ViewIconButton
          key={`view-${id}-${index}`}
          navId={id}
          baseUri={baseUri}
        />
      );
    case "edit":
      return (
        <EditIconButton
          key={`edit-${id}-${index}`}
          navId={id}
          baseUri={baseUri}
        />
      );
    case "delete":
      return (
        <DeleteIconButton
          key={`delete-${id}-${index}`}
          navId={id}
          baseUri={baseUri}
        />
      );
    default:
      return null;
  }
};

// interface RowData {
//   [key: string]: unknown;
//   actionButtons: ReactNode;
// }

// const renderActionButtons = (rowData: RowData) => {
//   return rowData.actionButtons;
// };

export default PrimeGridView;
