<?php

declare(strict_types=1);

namespace PpPhp\Core;

/**
 * Pagination API – generate pagination controls and fetch sliced records.
 *
 * Usage:
 *   $pager = new Pagination($totalRows, $page, $perPage);
 *   $pager->setBaseUrl('/users/list');
 *   $pager->setQueryParams(['sort' => 'name']);
 *   $html = $pager->render();
 *   $data = DB::Query("SELECT * FROM users LIMIT {$pager->offset()}, {$pager->limit()}");
 */
class Pagination
{
    private int $totalRows;
    private int $currentPage;
    private int $perPage;
    private int $totalPages;
    private int $offset;
    private string $baseUrl = '';
    private array $queryParams = [];
    private string $wrapperClass = 'pagination';
    private string $activeClass = 'active';
    private string $disabledClass = 'disabled';
    private int $maxPagesToShow = 5;

    /**
     * @param int $totalRows   Total number of records in the result set.
     * @param int $currentPage Current page number (1‑based).
     * @param int $perPage     Number of records per page.
     */
    public function __construct(int $totalRows, int $currentPage, int $perPage)
    {
        $this->totalRows = max(0, $totalRows);
        $this->currentPage = max(1, $currentPage);
        $this->perPage = max(1, $perPage);
        $this->totalPages = (int) ceil($this->totalRows / $this->perPage);
        $this->offset = ($this->currentPage - 1) * $this->perPage;
    }

    /**
     * Set the base URL for pagination links (without query string).
     */
    public function setBaseUrl(string $url): self
    {
        $this->baseUrl = $url;
        return $this;
    }

    /**
     * Set additional query parameters to append to every pagination link.
     *
     * @param array $params Associative array of key => value.
     */
    public function setQueryParams(array $params): self
    {
        $this->queryParams = $params;
        return $this;
    }

    /**
     * Set CSS classes for the pagination container.
     */
    public function setWrapperClass(string $class): self
    {
        $this->wrapperClass = $class;
        return $this;
    }

    /**
     * Set CSS class for the active page link.
     */
    public function setActiveClass(string $class): self
    {
        $this->activeClass = $class;
        return $this;
    }

    /**
     * Set CSS class for disabled links.
     */
    public function setDisabledClass(string $class): self
    {
        $this->disabledClass = $class;
        return $this;
    }

    /**
     * Set maximum number of page links to show (excluding prev/next).
     */
    public function setMaxPagesToShow(int $max): self
    {
        $this->maxPagesToShow = max(1, $max);
        return $this;
    }

    /**
     * Return the LIMIT offset (for SQL queries).
     */
    public function offset(): int
    {
        return $this->offset;
    }

    /**
     * Return the LIMIT value (records per page).
     */
    public function limit(): int
    {
        return $this->perPage;
    }

    /**
     * Return the current page number.
     */
    public function currentPage(): int
    {
        return $this->currentPage;
    }

    /**
     * Return the total number of pages.
     */
    public function totalPages(): int
    {
        return $this->totalPages;
    }

    /**
     * Check if there is a next page.
     */
    public function hasNext(): bool
    {
        return $this->currentPage < $this->totalPages;
    }

    /**
     * Check if there is a previous page.
     */
    public function hasPrev(): bool
    {
        return $this->currentPage > 1;
    }

    /**
     * Generate the full HTML pagination control.
     */
    public function render(): string
    {
        if ($this->totalPages <= 1) {
            return '';
        }

        $links = $this->buildLinks();
        return '<nav aria-label="Page navigation"><ul class="' . htmlspecialchars($this->wrapperClass) . '">' . $links . '</ul></nav>';
    }

    /**
     * Build the inner <li> elements.
     */
    private function buildLinks(): string
    {
        $html = '';

        // Previous button
        $html .= $this->buildLinkItem(
            $this->hasPrev() ? $this->getPageUrl($this->currentPage - 1) : '#',
            '&laquo;',
            !$this->hasPrev(),
            'prev'
        );

        // First page (always show)
        if ($this->totalPages > 0) {
            $html .= $this->buildNumberItem(1);
        }

        // Page numbers (with ellipsis)
        $start = max(2, $this->currentPage - floor($this->maxPagesToShow / 2));
        $end = min($this->totalPages - 1, $start + $this->maxPagesToShow - 1);
        if ($start > 2) {
            $html .= $this->buildEllipsis();
        }
        for ($i = $start; $i <= $end; $i++) {
            $html .= $this->buildNumberItem($i);
        }
        if ($end < $this->totalPages - 1) {
            $html .= $this->buildEllipsis();
        }

        // Last page
        if ($this->totalPages > 1) {
            $html .= $this->buildNumberItem($this->totalPages);
        }

        // Next button
        $html .= $this->buildLinkItem(
            $this->hasNext() ? $this->getPageUrl($this->currentPage + 1) : '#',
            '&raquo;',
            !$this->hasNext(),
            'next'
        );

        return $html;
    }

    private function buildNumberItem(int $page): string
    {
        $isActive = ($page === $this->currentPage);
        $url = $this->getPageUrl($page);
        $class = $isActive ? $this->activeClass : '';
        return $this->buildLinkItem($url, (string) $page, false, $class);
    }

    private function buildLinkItem(string $url, string $label, bool $disabled = false, string $extraClass = ''): string
    {
        $class = trim($extraClass . ($disabled ? ' ' . $this->disabledClass : ''));
        $classAttr = $class ? ' class="' . htmlspecialchars($class) . '"' : '';
        $href = $disabled ? ' href="javascript:void(0)"' : ' href="' . htmlspecialchars($url) . '"';
        return '<li' . $classAttr . '><a' . $href . '>' . $label . '</a></li>';
    }

    private function buildEllipsis(): string
    {
        return '<li class="disabled"><span>&hellip;</span></li>';
    }

    private function getPageUrl(int $page): string
    {
        $params = $this->queryParams;
        $params['page'] = $page;
        $query = http_build_query($params);
        return rtrim($this->baseUrl, '?') . ($query ? '?' . $query : '');
    }
}
