import {
  DataTable,
  DataTableFilterMeta,
  DataTableProps,
  DataTableValueArray,
} from "primereact/datatable";
import { OSIcon } from "../icon";
import {
  Children,
  cloneElement,
  createContext,
  forwardRef,
  ReactElement,
  useCallback,
  useRef,
  useState,
} from "react";
import { Column, ColumnProps } from "primereact/column";
import { Skeleton } from "primereact/skeleton";
import { Maximize, MaximizeArea, MaximizeButton } from "../maximize";
import { OSLabel } from "../label";
import { classNames } from "primereact/utils";
import { OSButton } from "../button";
import { ExportOptions } from "./props";
import { goGet } from "../../utils/goFetch";
import { Toast } from "primereact/toast";

export type OSDataTableProps<TValue extends DataTableValueArray> = DataTableProps<TValue> & {
  // Values that aren't used but should be accepted to prevent DOM errors when exploding data provider.
  rawData?: any;
  totalPages?: number | undefined;
  enableExport?: boolean;
  exportOptions?: ExportOptions;
  extraButtons?: ReactElement;
  setFilters?: (filters: DataTableFilterMeta) => void;
  /** Hide the skeleton when the form is in a loading state. Skeleton should always show on first load */
  hidePageRefreshWhenLoading?: boolean;
};


export const OSDataTableContext = createContext<OSDataTableProps<any> | null>(null);


export const OSDataTable = forwardRef(function OSDataTable<TValue extends DataTableValueArray>(dataTableProps: OSDataTableProps<TValue>,
  ref: React.ForwardedRef<Readonly<DataTable<TValue>>>) {
  const {
    rawData,
    className,
    totalPages,
    children,
    loading,
    enableExport,
    exportOptions,
    extraButtons,
    hidePageRefreshWhenLoading = false,
    setFilters,
    ...props
  } = dataTableProps;

  const maximize = useRef<Maximize>(null);
  const [exportLoading, setExportLoading] = useState(false);

  // Handle custom skeleton-based loading animation
  let isSkeleton: boolean = false;
  if (loading && !hidePageRefreshWhenLoading && props.rows) {
    isSkeleton = true;
    let data: DataTableValueArray = [];
    for (let index = 0; index < props.rows; index++) {
      data.push({});
    }
    props.value = data as TValue;
  }

  // Configure defaults for Columns - no other solution seemed to work (HOC, passthrough etc.)
  const modifiedChildren = Children.map(children, (child: any) => {
    if (child?.type?.name === Column.name) {
      return cloneElement<ColumnProps>(child, {
        showFilterMenu: child.props.showFilterMenu ?? false,
        body: isSkeleton ? <Skeleton height="2rem" /> : child.props.body,
      });
    }
    return child;
  });

  const getHeaderText = useCallback(() => {
    const { first = 0, rows = 0, totalRecords = 0 } = props;
    let last = (Math.floor(first / rows) + 1) * rows;
    if (last > totalRecords) {
      last = totalRecords;
    }
    if (loading && hidePageRefreshWhenLoading) return "Refreshing...";
    if (loading) return "";
    if (last === 0) return ""; // Table itself shows 'No Records found', so no need for duplication.
    if (props.paginator)
      return `Showing ${first + 1} to ${last} out of ${totalRecords} total records`;
    return `${totalRecords} records found`;
  }, [props.first, props.rows, props.totalRecords, loading, props.paginator]);

  const toast = useRef<Toast>(null);

  return (<OSDataTableContext.Provider value={dataTableProps}>
    <div className="flex flex-col">
      <Toast ref={toast} />
      <div className="flex relative pl-2">
        <OSLabel label={getHeaderText()} tag={"small"} />
        <div className="grow" />
        {extraButtons && <div className="p1 mr-2">{extraButtons}</div>}
        {enableExport && exportOptions?.endpoint !== undefined && (
          <div className="p1 mr-2">
            <OSButton
              label="Export"
              icon={{
                identifier: exportLoading ? "spinner-third" : "file-invoice",
                animation: exportLoading ? "spin" : undefined,
              }}
              disabled={exportLoading}
              onClick={() => {
                if (exportOptions.endpoint === undefined) {
                  return;
                }
                setExportLoading(true);
                goGet<{ success: boolean }>(exportOptions.endpoint)
                  .then((data) => {
                    if (data.success) {
                      toast.current?.show({
                        severity: "success",
                        summary: "Report Export Started",
                        detail: (
                          <>
                            <p>
                              This report will generate in the background and we
                              will notify you once it is ready.
                            </p>
                            <p>
                              Click{" "}
                              <a href="/report/generated-reports">
                                here to view the report's progress and to
                                download when ready
                              </a>
                            </p>
                          </>
                        ),
                        // life: 3000,
                        sticky: true,
                      });
                    } else {
                      toast.current?.show({
                        severity: "error",
                        summary: "Error",
                        detail:
                          "Something went wrong preparing this report, please try again",
                        // life: 3000,
                        sticky: true,
                      });
                    }
                  })
                  .catch(() => {
                    toast.current?.show({
                      severity: "error",
                      summary: "Error",
                      detail: (
                        <p>
                          Something went wrong preparing this report, please try
                          again.
                        </p>
                      ),
                      // life: 3000,
                      sticky: true,
                    });
                  })
                  .finally(() => {
                    setExportLoading(false);
                  });
              }}
            />
          </div>
        )}
        <MaximizeButton maximizeRef={maximize} />
      </div>
      <MaximizeArea ref={maximize}>
        <DataTable
          ref={ref}
          className={classNames("os-data-table", className)}
          loading={false}
          filterDisplay="row"
          scrollable
          stripedRows
          children={modifiedChildren}
          filterClearIcon={
            <OSIcon
              identifier="filter-circle-xmark"
              colour="var(--colour-danger-red)"
              tooltip="Clear filter"
              size={"lg"}
            />
          }
          sortIcon={(args) => {
            return (
              <a
                className={
                  args.sortOrder === 1
                    ? "asc"
                    : args.sortOrder === -1
                      ? "desc"
                      : "sort"
                }
              />
            );
          }}
          lazy
          {...props}
        />
      </MaximizeArea>
    </div>
  </OSDataTableContext.Provider >
  );
});
