/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { useState, useRef, useEffect } from "react";
import {
  Autocomplete,
  FormControl,
  IconButton,
  InputBase,
  MenuItem,
  Pagination,
  Paper,
  Stack,
  TextField,
  Typography
} from "@mui/material";
import { useTheme } from "@emotion/react";
import { KeyboardArrowLeft, KeyboardArrowRight } from "@mui/icons-material";
import { LightTooltip } from "../muiComponents";
import { useLocation, useNavigate } from "react-router-dom";
import { isEmpty } from "../../utils/Utils";
import Messages from "@utils/Messages";
import { toast } from "react-toastify";
import { GetNumberSuffix } from "@components/commonComponents";
import { pageSizeOptions } from "@utils/Enum";
import { createFilterOptions } from "@mui/material/Autocomplete";
import { styled } from "@mui/material/styles";

const filter = createFilterOptions<any>();

export const StyledPagination = styled(Pagination)`
  display: flex;
  justify-content: center;
  margin-top: 1rem;
`;

interface PaginationProps {
  isServerSide?: boolean;
  getPageCount?: () => number;
  previousPage?: () => void;
  nextPage?: () => void;
  setPageIndex?: (index: number) => void;
  setPageSize?: (size: number) => void;
  getRowModel?: () => any;
  pagination: { pageIndex: number; pageSize: number };
  setRowSelection?: (selection: any) => void;
  memoizedData: any[];
  isFirstPage?: boolean;
  setPagination?: (pagination: any) => void;
  goToPageVal?: string;
  setGoToPageVal?: React.Dispatch<React.SetStateAction<string>>;
  isEmptyData?: boolean;
  setIsEmptyData?: any;
  showGoToPage?: boolean;
  totalCount?: number;
  /** Client mode: notify parent when page size changes (user action). */
  onClientPageSizeChange?: (pageSize: number) => void;
}

const TablePagination: React.FC<PaginationProps> = ({
  isServerSide = false,
  getPageCount: _getPageCount,
  previousPage,
  nextPage,
  setPageIndex,
  setPageSize,
  getRowModel: _getRowModel,
  pagination,
  setRowSelection,
  memoizedData,
  isFirstPage,
  setPagination,
  goToPageVal,
  setGoToPageVal,
  isEmptyData,
  setIsEmptyData,
  showGoToPage = false,
  totalCount,
  onClientPageSizeChange
}) => {
  const theme: any = useTheme();
  const location = useLocation();
  const navigate = useNavigate();
  const searchParams = new URLSearchParams(location.search);
  const { pageIndex, pageSize } = pagination;

  /** Client mode: optional approximate total so Next/footer work before all rows load. */
  const clientEffectiveTotal =
    !isServerSide &&
    typeof totalCount === "number" &&
    totalCount >= 0
      ? totalCount
      : memoizedData.length;

  const [value, setValue] = useState<any>({
    label:
      searchParams.get("pageLimit") ??
      pageSize.toString() ??
      pageSizeOptions[0].label
  });
  const [limit, setLimit] = useState<number>(
    searchParams.get("pageLimit")
      ? Number(searchParams.get("pageLimit"))
      : pageSize
  );

  const [pageFrom, setPageFrom] = useState<number>(
    isServerSide && searchParams.get("pageOffset")
      ? Number(searchParams.get("pageOffset")) + 1
      : isServerSide
      ? 1
      : pageIndex * pageSize + 1
  );
  const [pageTo, setPageTo] = useState<number>(
    isServerSide && searchParams.get("pageOffset")
      ? Number(searchParams.get("pageOffset")) + Number(limit)
      : isServerSide
      ? Number(limit)
      : Math.min((pageIndex + 1) * pageSize, clientEffectiveTotal)
  );
  const [offset, setOffset] = useState<number>(
    isServerSide && searchParams.get("pageOffset")
      ? Number(searchParams.get("pageOffset"))
      : pageIndex * pageSize
  );
  const [activePage, setActivePage] = useState<number>(
    isServerSide ? Math.floor(offset / Number(limit)) + 1 : pageIndex + 1
  );

  const [pendingGoToPageVal, setPendingGoToPageVal] = useState<string>("");
  const [goToPageTrigger, setGoToPageTrigger] = useState<string>("");
  const toastId = useRef<any>(null);

  useEffect(() => {
    if (isServerSide) {
      setValue({ label: searchParams.get("pageLimit") ?? limit.toString() });
      setLimit(Number(searchParams.get("pageLimit") ?? limit));
      setOffset(Number(searchParams.get("pageOffset") ?? pageIndex * pageSize));
      setPageFrom(
        Number(searchParams.get("pageOffset") ?? pageIndex * pageSize) + 1
      );
      setPageTo(
        Number(searchParams.get("pageOffset") ?? pageIndex * pageSize) +
          Number(limit)
      );
    }
  }, [isServerSide, location.search, limit, pageIndex, pageSize]);

  // Do not force offset to 0 based solely on isFirstPage; rely on URL (pageOffset)

  useEffect(() => {
    if (isServerSide) {
      const sp = new URLSearchParams(location.search);
      const effLimit = Number(sp.get("pageLimit") ?? limit);
      const effOffset = Number(sp.get("pageOffset") ?? 0);
      setActivePage(Math.floor(effOffset / effLimit) + 1);
    } else {
      setActivePage(pageIndex + 1);
    }
  }, [isServerSide, location.search, limit, pageIndex]);

  const handlePageSizeChange = (newValue: any) => {
    const newPageSize: any = Number(newValue.label);
    setValue({ label: newValue.label });
    if (isServerSide) {
      setLimit(newPageSize);
      setPageFrom(1);
      setPageTo(newPageSize);
      setActivePage(1);
      setOffset(0);
      searchParams.delete("pageOffset");
      searchParams.set("pageLimit", newPageSize);
      navigate({ search: searchParams.toString() });
      setRowSelection?.({});
      setPagination?.((prev: any) => ({ ...prev, pageIndex: 0, pageSize: newPageSize }));
      // Clear Go-to state on page size change
      setGoToPageVal?.("");
      setPendingGoToPageVal("");
      setGoToPageTrigger("");
    } else {
      setPageSize?.(newPageSize);
      setPageIndex?.(0);
      setLimit(newPageSize);
      setPageFrom(1);
      setPageTo(Math.min(newPageSize, clientEffectiveTotal));
      if (typeof onClientPageSizeChange === "function") {
        onClientPageSizeChange(newPageSize);
      }
      // Clear Go-to state on page size change
      setGoToPageVal?.("");
      setPendingGoToPageVal("");
      setGoToPageTrigger("");
    }
  };

  const handleGoToPage = () => {
    const goToPage = parseInt(goToPageVal || pendingGoToPageVal);
    if (isNaN(goToPage) || goToPage < 1) return;

    if (isServerSide) {
      // Guard based on approximate total count if available: pages are 1..ceil(totalCount/limit)
      if (typeof totalCount === "number" && totalCount >= 0) {
        const maxPage = Math.max(1, Math.ceil(totalCount / Number(limit || 1)));
        if (goToPage > maxPage) {
          toast.dismiss(toastId.current);
          toastId.current = toast.info(
            <>
              {Messages.search.noRecordForPage}
              <b>
                <GetNumberSuffix number={goToPage} sup={true} />
              </b>{" "}
              page
            </>
          );
          setGoToPageVal?.("");
          setPendingGoToPageVal("");
          return;
        }
      }
      const newOffset = (goToPage - 1) * limit;
      if (newOffset === offset) {
        toast.dismiss(toastId.current);
        toastId.current = toast.info(`${Messages.search.onSamePage}`);
        setGoToPageVal?.("");
        setPendingGoToPageVal("");
        return;
      }
      setOffset(newOffset);
      setPageFrom(newOffset + 1);
      setPageTo(newOffset + limit);
      setActivePage(goToPage);
      setPageIndex?.(goToPage - 1);
      setRowSelection?.({});
      searchParams.set("pageOffset", newOffset.toString());
      navigate({ search: searchParams.toString() });
      setGoToPageVal?.("");
      setPendingGoToPageVal("");
    } else {
      const maxPage = Math.max(
        1,
        Math.ceil(clientEffectiveTotal / pageSize) || 1
      );
      if (goToPage > maxPage) {
        toast.dismiss(toastId.current);
        toastId.current = toast.info(
          <>
            {Messages.search.noRecordForPage} page "<strong>{goToPage}</strong>"
          </>
        );
        setGoToPageVal?.("");
        setPendingGoToPageVal("");
        setGoToPageTrigger("");
        return;
      }
      if (goToPage - 1 === pageIndex) {
        toast.dismiss(toastId.current);
        toastId.current = toast.info(`${Messages.search.onSamePage}`);
        setGoToPageVal?.("");
        setPendingGoToPageVal("");
        setGoToPageTrigger("");
        return;
      }
      setPageIndex?.(goToPage - 1);
      setPageFrom((goToPage - 1) * pageSize + 1);
      setPageTo(Math.min(goToPage * pageSize, clientEffectiveTotal));
      setGoToPageVal?.("");
      setPendingGoToPageVal("");
      setGoToPageTrigger("");
    }
  };

  useEffect(() => {
    if ((goToPageVal || goToPageTrigger || isEmptyData) && setPageIndex) {
      handleGoToPage();
      if (typeof setIsEmptyData === "function") {
        setIsEmptyData(false);
      }
      setGoToPageTrigger("");
    }
  }, [goToPageVal, goToPageTrigger, isEmptyData]);

  const handlePreviousPage = () => {
    if (isServerSide) {
      const prevOffset = offset - limit;
      const safePrevOffset = prevOffset < 0 ? 0 : prevOffset;
      setOffset(safePrevOffset);
      setPageFrom(safePrevOffset + 1);
      setPageTo(safePrevOffset + limit);
      setPagination?.((prev: any) => ({
        ...prev,
        pageIndex: prev.pageIndex - 1
      }));
      setRowSelection?.({});
      // Always reflect new offset in URL (including 0) so fetch effect triggers correctly
      searchParams.set("pageOffset", safePrevOffset.toString());
      navigate({ search: searchParams.toString() });
    } else {
      previousPage?.();
      const newPageIndex = Math.max(0, pageIndex - 1);
      setPageFrom(newPageIndex * pageSize + 1);
      setPageTo(
        Math.min((newPageIndex + 1) * pageSize, clientEffectiveTotal)
      );
    }
    setGoToPageVal?.("");
  };

  const handleNextPage = () => {
    if (isServerSide) {
      const nextOffset = offset + limit;
      setOffset(nextOffset);
      setPageFrom(nextOffset + 1);
      setPageTo(nextOffset + limit);
      setPagination?.((prev: any) => ({
        ...prev,
        pageIndex: prev.pageIndex + 1
      }));
      setRowSelection?.({});
      searchParams.set("pageOffset", nextOffset.toString());
      navigate({ search: searchParams.toString() });
    } else {
      nextPage?.();
      setPageFrom((pageIndex + 1) * pageSize + 1);
      setPageTo(
        Math.min((pageIndex + 2) * pageSize, clientEffectiveTotal)
      );
    }
    setGoToPageVal?.("");
  };

  // In server-side mode, rely on computed offset to decide first page
  const isPreviousDisabled = isServerSide ? offset === 0 : pageIndex === 0;
  // In server-side mode, disable Next when fewer than limit rows returned (last page)
  const isNextDisabled = isServerSide
    ? (typeof totalCount === "number" && totalCount >= 0
        ? offset + limit >= totalCount
        : memoizedData.length < limit)
    : clientEffectiveTotal === 0
    ? true
    : pageIndex + 1 >= Math.ceil(clientEffectiveTotal / pageSize);

  /** Client-side: optional totalCount (approximate) or loaded row count. */
  const totalDatasetRows = isServerSide
    ? typeof totalCount === "number" && totalCount >= 0
      ? totalCount
      : memoizedData.length
    : clientEffectiveTotal;

  const displayFrom = isServerSide
    ? pageFrom
    : totalDatasetRows === 0
    ? 0
    : Math.min(pageIndex * pageSize + 1, totalDatasetRows);
  const displayTo = isServerSide
    ? pageTo
    : Math.min((pageIndex + 1) * pageSize, totalDatasetRows);

  return (
    <Stack
      spacing={{ xs: 1, sm: 2 }}
      direction="row"
      useFlexGap
      flexWrap="wrap"
      justifyContent="space-between"
      alignItems="center"
      className="table-pagination"
    >
      <div>
        <span className="text-grey">
          Showing <u>{totalDatasetRows.toLocaleString()} records</u> From{" "}
          {displayFrom} - {displayTo}
        </span>
      </div>

      <div className="table-pagination-filters">
        <Stack
          className="table-pagination-filters-box"
          direction="row"
          alignItems="center"
          gap="0.5rem"
        >
          <Typography
            className="text-grey"
            whiteSpace="nowrap"
            fontWeight="400"
            lineHeight="32px"
          >
            Page Limit :
          </Typography>
          <FormControl fullWidth size="small">
            <Autocomplete
              value={value}
              className="pagination-page-limit"
              disableClearable
              onChange={(_, newValue: any) => {
                if (typeof newValue === "string") {
                  handlePageSizeChange({ label: newValue });
                } else if (newValue?.inputValue) {
                  handlePageSizeChange({ label: newValue.inputValue });
                } else if (newValue) {
                  handlePageSizeChange(newValue);
                }
              }}
              filterOptions={(options, params) => {
                const filtered = filter(options, params);
                const { inputValue } = params;
                const isExisting = options.some(
                  (option) => inputValue === option.label
                );
                if (inputValue !== "" && setPageSize && !isExisting) {
                  filtered.push({ inputValue, label: `${inputValue}` });
                }
                return filtered;
              }}
              selectOnFocus
              clearOnBlur
              handleHomeEndKeys
              id="Page Limit:"
              options={pageSizeOptions}
              size="small"
              getOptionLabel={(option) =>
                typeof option === "string"
                  ? option
                  : option.inputValue || option.label
              }
              renderOption={(props, option) => (
                <MenuItem {...props} value={option.label}>
                  {option.label}
                </MenuItem>
              )}
              sx={{
                width: "78px",
                "& .MuiOutlinedInput-root.MuiInputBase-sizeSmall .MuiAutocomplete-input":
                  {
                    padding: "0 4px !important",
                    height: "15px !important"
                  }
              }}
              freeSolo
              renderInput={(params) => <TextField type="number" {...params} />}
            />
          </FormControl>
        </Stack>

        {(isServerSide
          ? !isFirstPage || memoizedData.length >= limit
          : memoizedData.length >= pageSize) && (
          <>
            {showGoToPage && (
              <Stack className="table-pagination-filters-box">
                <Paper
                  component="form"
                  elevation={0}
                  className="table-pagination-gotopage-paper"
                  onSubmit={(e) => e.preventDefault()}
                >
                  <InputBase
                    placeholder="Go to page:"
                    type="number"
                    inputProps={{
                      "aria-label": "Go to page:",
                      style: { width: "100%" }
                    }}
                    size="small"
                    onChange={(e) => {
                      setPendingGoToPageVal(e.target.value);
                    }}
                    onKeyUp={(e) => {
                      const goToPage = parseInt(e.currentTarget.value);
                      const anyEvent: any = e as any;
                      if ((anyEvent.key === "Enter" || anyEvent.keyCode === 13) && !isNaN(goToPage) && goToPage >= 1) {
                        setGoToPageVal?.(e.currentTarget.value);
                        setGoToPageTrigger(e.currentTarget.value);
                      }
                    }}
                    className="table-pagination-gotopage-input"
                    value={pendingGoToPageVal}
                  />
                  <LightTooltip title="Goto Page">
                    <IconButton
                      type="button"
                      size="small"
                      className={`${
                        !isEmpty(pendingGoToPageVal)
                          ? "cursor-pointer"
                          : "cursor-not-allowed"
                      } table-pagination-gotopage-button`}
                      aria-label="search"
                      onClick={() => {
                        if (!isEmpty(pendingGoToPageVal)) {
                          setGoToPageTrigger(pendingGoToPageVal);
                          handleGoToPage();
                        }
                      }}
                      disabled={isEmpty(pendingGoToPageVal)}
                    >
                      Go
                    </IconButton>
                  </LightTooltip>
                </Paper>
              </Stack>
            )}

            <Stack flexDirection="row" alignItems="center">
              <LightTooltip title="Previous">
                <IconButton
                  size="small"
                  className="pagination-page-change-btn"
                  onClick={handlePreviousPage}
                  disabled={isPreviousDisabled}
                  aria-label="previous page"
                >
                  {theme.direction === "rtl" ? (
                    <KeyboardArrowRight />
                  ) : (
                    <KeyboardArrowLeft />
                  )}
                </IconButton>
              </LightTooltip>

              <LightTooltip title={`Page ${activePage}`}>
                <IconButton size="small" className="table-pagination-page">
                  {activePage}
                </IconButton>
              </LightTooltip>

              <LightTooltip title="Next">
                <IconButton
                  size="small"
                  className="pagination-page-change-btn"
                  onClick={handleNextPage}
                  disabled={isNextDisabled}
                  aria-label="next page"
                >
                  {theme.direction === "rtl" ? (
                    <KeyboardArrowLeft />
                  ) : (
                    <KeyboardArrowRight />
                  )}
                </IconButton>
              </LightTooltip>
            </Stack>
          </>
        )}
      </div>
    </Stack>
  );
};

export default TablePagination;
