import type { ReactNode } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import './pagination.css';

export type PaginationVariant = 'numbers' | 'load-more' | 'infinite-scroll' | 'detailed';
export type PaginationSize = 'sm' | 'md' | 'lg';
type PageItem = number | 'ellipsis';

function clamp(n: number, min: number, max: number) {
  return Math.min(max, Math.max(min, n));
}

function getPageItems(totalPages: number, currentPage: number, siblings = 1, boundaries = 1): PageItem[] {
  if (totalPages <= 0) return [];
  if (totalPages <= (boundaries * 2 + siblings * 2 + 3)) {
    return Array.from({ length: totalPages }, (_, i) => i + 1);
  }

  const current = clamp(currentPage, 1, totalPages);

  const leftBoundary = Array.from({ length: boundaries }, (_, i) => i + 1);
  const rightBoundary = Array.from({ length: boundaries }, (_, i) => totalPages - boundaries + 1 + i);

  const start = Math.max(boundaries + 1, current - siblings);
  const end = Math.min(totalPages - boundaries, current + siblings);

  const middle = Array.from({ length: Math.max(0, end - start + 1) }, (_, i) => start + i);

  const items: PageItem[] = [];
  items.push(...leftBoundary);

  if (start > boundaries + 1) items.push('ellipsis');
  items.push(...middle);
  if (end < totalPages - boundaries) items.push('ellipsis');

  items.push(...rightBoundary);
  return items;
}

export type PaginationProps = {
  currentPage?: number;
  totalPages?: number;
  totalItems?: number;
  pageSize?: number;
  pageSizeOptions?: number[];
  variant?: PaginationVariant;
  /** Control sizing: sm, md (default), lg */
  size?: PaginationSize;
  /** Render pill/rounded pagination buttons. */
  rounded?: boolean;
  showFirstLast?: boolean;
  prevButton?: (ctx: { disabled: boolean; onClick: () => void }) => ReactNode;
  nextButton?: (ctx: { disabled: boolean; onClick: () => void }) => ReactNode;
  onPageChange?: (page: number) => void;
  onPageSizeChange?: (size: number) => void;
};

export function Pagination({
  currentPage: controlledPage,
  totalPages: totalPagesProp,
  totalItems,
  pageSize: pageSizeProp = 10,
  pageSizeOptions = [10, 25, 50, 100],
  variant = 'numbers',
  size = 'md',
  rounded = false,
  showFirstLast = false,
  prevButton,
  nextButton,
  onPageChange,
  onPageSizeChange,
}: PaginationProps) {
  const [innerPage, setInnerPage] = useState(1);
  const [pageSize, setPageSize] = useState(pageSizeProp);
  const isControlled = controlledPage !== undefined;
  const currentPage = isControlled ? controlledPage! : innerPage;

  const totalPagesResolved = useMemo(() => {
    if (totalPagesProp != null) return totalPagesProp;
    if (totalItems != null && pageSize) return Math.max(1, Math.ceil(totalItems / pageSize));
    return undefined;
  }, [totalItems, pageSize, totalPagesProp]);

  useEffect(() => {
    setPageSize(pageSizeProp);
  }, [pageSizeProp]);

  const setPage = useCallback(
    (page: number) => {
      const total = totalPagesResolved;
      if (page < 1 || (total != null && page > total)) return;
      if (!isControlled) setInnerPage(page);
      onPageChange?.(page);
    },
    [isControlled, onPageChange, totalPagesResolved]
  );

  useEffect(() => {
    const total = totalPagesResolved;
    if (total && currentPage > total) setPage(total);
  }, [currentPage, setPage, totalPagesResolved]);

  const pageItems = useMemo(() => {
    if (!totalPagesResolved) return [];
    // Compact numeric range with ellipses to avoid overflow on large totals
    return getPageItems(totalPagesResolved, currentPage, 1, 1);
  }, [currentPage, totalPagesResolved]);

  const rangeStart =
    totalItems == null ? undefined : (currentPage - 1) * pageSize + 1;
  const rangeEnd =
    totalItems == null ? undefined : Math.min(totalItems, currentPage * pageSize);

  const onPageSizeSelect = useCallback(
    (newSize: number) => {
      if (!newSize || newSize === pageSize) return;
      setPageSize(newSize);
      onPageSizeChange?.(newSize);
      setPage(1);
    },
    [onPageSizeChange, pageSize, setPage]
  );

  useEffect(() => {
    if (variant !== 'infinite-scroll') return;
    const threshold = 300;
    const onScroll = () => {
      if (window.innerHeight + window.scrollY >= document.body.offsetHeight - threshold) {
        setPage(currentPage + 1);
      }
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, [variant, currentPage, setPage]);

  const sizeClass = `ccl-pagination--${size}`;
  const roundedClass = rounded ? 'ccl-pagination--rounded' : '';

  if (variant === 'numbers') {
    return (
      <nav
        className={['ccl-pagination', sizeClass, roundedClass].filter(Boolean).join(' ')}
        role="navigation"
        aria-label="Pagination"
      >
        <button
          type="button"
          className="ccl-pagination__btn"
          disabled={currentPage === 1}
          onClick={() => setPage(currentPage - 1)}
          aria-label="Go to previous page"
        >
          ‹
        </button>

        {showFirstLast ? (
          <button
            type="button"
            className="ccl-pagination__btn"
            disabled={currentPage === 1}
            onClick={() => setPage(1)}
            aria-label="Go to first page"
          >
            «
          </button>
        ) : null}

        {pageItems.map((item, idx) =>
          item === 'ellipsis' ? (
            <span key={`e-${idx}`} className="ccl-pagination__ellipsis" aria-hidden>
              …
            </span>
          ) : (
            <button
              key={item}
              type="button"
              className={`ccl-pagination__btn${item === currentPage ? ' active' : ''}`}
              onClick={() => setPage(item)}
              aria-current={item === currentPage ? 'page' : undefined}
              aria-label={`Go to page ${item}`}
            >
              {item}
            </button>
          )
        )}

        {showFirstLast && totalPagesResolved ? (
          <button
            type="button"
            className="ccl-pagination__btn"
            disabled={currentPage === totalPagesResolved}
            onClick={() => setPage(totalPagesResolved)}
            aria-label="Go to last page"
          >
            »
          </button>
        ) : null}

        <button
          type="button"
          className="ccl-pagination__btn"
          disabled={!!totalPagesResolved && currentPage === totalPagesResolved}
          onClick={() => setPage(currentPage + 1)}
          aria-label="Go to next page"
        >
          ›
        </button>
      </nav>
    );
  }

  if (variant === 'load-more') {
    return (
      <div className={['ccl-pagination__load-more', sizeClass, roundedClass].filter(Boolean).join(' ')}>
        <button
          type="button"
          className="ccl-pagination__btn"
          onClick={() => setPage(currentPage + 1)}
          aria-label="Load more items"
        >
          Load More
        </button>
      </div>
    );
  }

  if (variant === 'infinite-scroll') {
    return <div className="ccl-pagination__load-more" aria-hidden style={{ height: 1 }} />;
  }

  return (
    <div
      className={['ccl-pagination-detailed-inline', sizeClass, roundedClass].filter(Boolean).join(' ')}
      role="navigation"
      aria-label="Pagination"
    >
      {prevButton ? (
        prevButton({ disabled: currentPage === 1, onClick: () => setPage(currentPage - 1) })
      ) : (
        <button
          type="button"
          className="ccl-pagination__btn"
          disabled={currentPage === 1}
          onClick={() => setPage(currentPage - 1)}
          aria-label="Go to previous page"
        >
          ‹
        </button>
      )}

      <div className="ccl-pagination-detailed-inline__range">
        {totalItems != null && rangeStart != null && rangeEnd != null ? (
          <span>
            {rangeStart} - {rangeEnd} of {totalItems}
          </span>
        ) : null}
      </div>

      {nextButton ? (
        nextButton({
          disabled: !!(totalPagesResolved && currentPage === totalPagesResolved),
          onClick: () => setPage(currentPage + 1),
        })
      ) : (
        <button
          type="button"
          className="ccl-pagination__btn"
          disabled={!!(totalPagesResolved && currentPage === totalPagesResolved)}
          onClick={() => setPage(currentPage + 1)}
          aria-label="Go to next page"
        >
          ›
        </button>
      )}

      <div className="ccl-pagination-detailed-inline__size">
        <label className="ccl-pagination-detailed__size-label" htmlFor="ccl-page-size">
          Records per page
        </label>
        <select
          id="ccl-page-size"
          className="ccl-pagination-detailed__size-select"
          value={pageSize}
          onChange={(e) => onPageSizeSelect(Number(e.target.value))}
        >
          {pageSizeOptions.map((opt) => (
            <option key={opt} value={opt}>
              {opt}
            </option>
          ))}
        </select>
      </div>
    </div>
  );
}
