<?php
/**
 * Length Aware Paginator Class
 *
 * @package ZenoChatbot\Framework\Database
 */

namespace ZenoChatbot\Framework\Database;


// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Length Aware Paginator Class
 */
class LengthAwarePaginator extends Paginator implements \JsonSerializable
{
    /**
     * The total number of items before slicing
     *
     * @var int
     */
    protected $total;

    /**
     * The last available page
     *
     * @var int
     */
    protected $lastPage;

    /**
     * Create a new paginator instance
     *
     * @param mixed $items
     * @param int $total
     * @param int $perPage
     * @param int|null $currentPage
     * @param array $options
     */
    public function __construct($items, $total, $perPage, $currentPage = null, array $options = [])
    {
        $this->total = $total;
        $this->lastPage = max((int) ceil($total / $perPage), 1);

        parent::__construct($items, $perPage, $currentPage, $options);
    }

    /**
     * Get the URL for the next page
     *
     * @return string|null
     */
    public function nextPageUrl()
    {
        if ($this->hasMorePages()) {
            return $this->url($this->currentPage() + 1);
        }

        return null;
    }

    /**
     * Determine if there are more items in the data source
     *
     * @return bool
     */
    public function hasMorePages()
    {
        return $this->currentPage() < $this->lastPage();
    }

    /**
     * Get the URL for the first page
     *
     * @return string
     */
    public function firstPageUrl()
    {
        return $this->url(1);
    }

    /**
     * Get the URL for the last page
     *
     * @return string
     */
    public function lastPageUrl()
    {
        return $this->url($this->lastPage());
    }

    /**
     * Get the total number of items being paginated
     *
     * @return int
     */
    public function total()
    {
        return $this->total;
    }

    /**
     * Get the last page
     *
     * @return int
     */
    public function lastPage()
    {
        return $this->lastPage;
    }

    /**
     * Get the page range for pagination links
     *
     * @param int $onEachSide
     * @return array
     */
    public function getUrlRange($start, $end)
    {
        $urls = [];

        for ($page = $start; $page <= $end; $page++) {
            $urls[$page] = $this->url($page);
        }

        return $urls;
    }

    /**
     * Create a range of pagination URLs
     *
     * @param int $start
     * @param int $end
     * @return array
     */
    public function range($start, $end)
    {
        return range($start, $end);
    }

    /**
     * Get the paginator links
     *
     * @param int $onEachSide
     * @return array
     */
    public function links($onEachSide = 3)
    {
        $currentPage = $this->currentPage();
        $lastPage = $this->lastPage();

        // If we have less pages than we want to show on each side, show all
        if ($lastPage <= ($onEachSide * 2) + 1) {
            return $this->getUrlRange(1, $lastPage);
        }

        // If we're close to the beginning
        if ($currentPage <= $onEachSide + 1) {
            return $this->getUrlRange(1, ($onEachSide * 2) + 1);
        }

        // If we're close to the end
        if ($currentPage >= $lastPage - $onEachSide) {
            return $this->getUrlRange($lastPage - ($onEachSide * 2), $lastPage);
        }

        // We're in the middle
        return $this->getUrlRange(
            $currentPage - $onEachSide,
            $currentPage + $onEachSide
        );
    }

    /**
     * Get pagination information
     *
     * @return array
     */
    public function getPaginationInfo()
    {
        return [
            'current_page' => $this->currentPage(),
            'per_page' => $this->perPage(),
            'total' => $this->total(),
            'last_page' => $this->lastPage(),
            'from' => $this->firstItem(),
            'to' => $this->lastItem(),
            'has_more_pages' => $this->hasMorePages(),
            'has_pages' => $this->hasPages(),
        ];
    }

    /**
     * Determine if the paginator is on the first page
     *
     * @return bool
     */
    public function onFirstPage()
    {
        return $this->currentPage() <= 1;
    }

    /**
     * Determine if the paginator is on the last page
     *
     * @return bool
     */
    public function onLastPage()
    {
        return $this->currentPage() >= $this->lastPage();
    }

    /**
     * Get the instance as an array
     *
     * @return array
     */
    public function toArray()
    {
        return [
            'current_page' => $this->currentPage(),
            'data' => $this->items->toArray(),
            'first_page_url' => $this->firstPageUrl(),
            'from' => $this->firstItem(),
            'last_page' => $this->lastPage(),
            'last_page_url' => $this->lastPageUrl(),
            'links' => $this->links(),
            'next_page_url' => $this->nextPageUrl(),
            'path' => $this->path(),
            'per_page' => $this->perPage(),
            'prev_page_url' => $this->previousPageUrl(),
            'to' => $this->lastItem(),
            'total' => $this->total(),
        ];
    }

    /**
     * Create a simple pagination view
     *
     * @param array $options
     * @return string
     */
    public function simplePaginate($options = [])
    {
        $options = array_merge([
            'previous_text' => '&laquo; Previous',
            'next_text' => 'Next &raquo;',
            'class' => 'pagination',
            'link_class' => 'page-link',
            'active_class' => 'active',
            'disabled_class' => 'disabled'
        ], $options);

        $html = '<nav><ul class="' . $options['class'] . '">';

        // Previous page link
        if ($this->currentPage() > 1) {
            $html .= '<li><a href="' . $this->previousPageUrl() . '" class="' . $options['link_class'] . '">' . $options['previous_text'] . '</a></li>';
        } else {
            $html .= '<li class="' . $options['disabled_class'] . '"><span>' . $options['previous_text'] . '</span></li>';
        }

        // Next page link
        if ($this->hasMorePages()) {
            $html .= '<li><a href="' . $this->nextPageUrl() . '" class="' . $options['link_class'] . '">' . $options['next_text'] . '</a></li>';
        } else {
            $html .= '<li class="' . $options['disabled_class'] . '"><span>' . $options['next_text'] . '</span></li>';
        }

        $html .= '</ul></nav>';

        return $html;
    }

    /**
     * Create a full pagination view
     *
     * @param array $options
     * @return string
     */
    public function render($options = [])
    {
        $options = array_merge([
            'previous_text' => '&laquo;',
            'next_text' => '&raquo;',
            'class' => 'pagination',
            'link_class' => 'page-link',
            'active_class' => 'active',
            'disabled_class' => 'disabled',
            'on_each_side' => 3
        ], $options);

        if (!$this->hasPages()) {
            return '';
        }

        $html = '<nav><ul class="' . $options['class'] . '">';

        // Previous page link
        if ($this->currentPage() > 1) {
            $html .= '<li><a href="' . $this->previousPageUrl() . '" class="' . $options['link_class'] . '">' . $options['previous_text'] . '</a></li>';
        } else {
            $html .= '<li class="' . $options['disabled_class'] . '"><span>' . $options['previous_text'] . '</span></li>';
        }

        // Page number links
        $links = $this->links($options['on_each_side']);
        foreach ($links as $page => $url) {
            if ($page == $this->currentPage()) {
                $html .= '<li class="' . $options['active_class'] . '"><span>' . $page . '</span></li>';
            } else {
                $html .= '<li><a href="' . $url . '" class="' . $options['link_class'] . '">' . $page . '</a></li>';
            }
        }

        // Next page link
        if ($this->hasMorePages()) {
            $html .= '<li><a href="' . $this->nextPageUrl() . '" class="' . $options['link_class'] . '">' . $options['next_text'] . '</a></li>';
        } else {
            $html .= '<li class="' . $options['disabled_class'] . '"><span>' . $options['next_text'] . '</span></li>';
        }

        $html .= '</ul></nav>';

        return $html;
    }

    /**
     * Get pagination summary text
     *
     * @return string
     */
    public function summary()
    {
        if ($this->total() == 0) {
            return 'No results found';
        }

        $from = $this->firstItem();
        $to = $this->lastItem();
        $total = $this->total();

        return "Showing {$from} to {$to} of {$total} results";
    }

    /**
     * Transform each item in the slice of items using a callback
     *
     * @param callable $callback
     * @return $this
     */
    public function through(callable $callback)
    {
        $this->items = $this->items->map($callback);

        return $this;
    }

    /**
     * Resolve the current page from request
     *
     * @param string $pageName
     * @return int
     */
    public static function resolveCurrentPage($pageName = 'page')
    {
        // Try to get request from application container
        try {
            if (function_exists('app')) {
                $app = app();
                if ($app->bound('request')) {
                    $request = $app->make('request');
                    return $request->page($pageName);
                }
            }
        } catch (\Exception $e) {
            // Fallback to direct access
        }

        $page = $_GET[$pageName] ?? 1;
        return max(1, (int) $page);
    }

    /**
     * Resolve the current path from request
     *
     * @return string
     */
    public static function resolveCurrentPath()
    {
        // Try to get request from application container
        try {
            if (function_exists('app')) {
                $app = app();
                if ($app->bound('request')) {
                    $request = $app->make('request');
                    return $request->path();
                }
            }
        } catch (\Exception $e) {
            // Fallback to direct access
        }

        return strtok($_SERVER['REQUEST_URI'] ?? '/', '?');
    }

    /**
     * Convert paginator to simple REST API response format
     *
     * @return array
     */
    public function toRestResponse(): array
    {
        return [
            'current_page' => $this->currentPage(),
            'data' => $this->items->toArray(),
            'first_page_url' => $this->url(1),
            'from' => $this->firstItem(),
            'last_page' => $this->lastPage(),
            'last_page_url' => $this->url($this->lastPage()),
            'links' => $this->linkCollection(),
            'next_page_url' => $this->nextPageUrl(),
            'path' => $this->path(),
            'per_page' => (string) $this->perPage(),
            'prev_page_url' => $this->previousPageUrl(),
            'to' => $this->lastItem(),
            'total' => $this->total(),
        ];
    }

    /**
     * Get the pagination links collection
     *
     * @return array
     */
    protected function linkCollection(): array
    {
        $links = [];

        // Previous link
        $links[] = [
            'url' => $this->previousPageUrl(),
            'label' => 'pagination.previous',
            'active' => false,
        ];

        // Page links (simplified - just show current page)
        $links[] = [
            'url' => $this->url($this->currentPage()),
            'label' => (string) $this->currentPage(),
            'active' => true,
        ];

        // Next link
        $links[] = [
            'url' => $this->nextPageUrl(),
            'label' => 'pagination.next',
            'active' => false,
        ];

        return $links;
    }

    /**
     * Convert to JSON for REST API
     *
     * @param int $options
     * @return string
     */
    public function toJson($options = 0)
    {
        return json_encode($this->jsonSerialize(), $options);
    }

    /**
     * Specify data which should be serialized to JSON
     * This is called when the paginator is returned from a controller
     *
     * @return array
     */
    public function jsonSerialize(): array
    {
        return [
            'conversations' => $this->items->toArray(),
            'pagination' => [
                'current_page' => $this->currentPage(),
                'per_page' => $this->perPage(),
                'total' => $this->total(),
                'last_page' => $this->lastPage(),
                'from' => $this->firstItem(),
                'to' => $this->lastItem(),
                'has_more_pages' => $this->hasMorePages()
            ],
            'success' => true
        ];
    }

}
