<?php

declare(strict_types=1);

namespace App\Framework\Pagination\ValueObjects;

/**
 * Pagination metadata
 */
final readonly class PaginationMeta
{
    public function __construct(
        public int $resultCount,
        public int $limit,
        public bool $hasMore,
        public bool $hasPrevious,
        public PaginationType $type,
        public ?int $totalCount = null,
        public ?int $totalPages = null,
        public ?int $currentPage = null,
        public ?Cursor $nextCursor = null,
        public ?Cursor $previousCursor = null,
        public ?string $sortField = null,
        public Direction $direction = Direction::ASC
    ) {
    }

    /**
     * Create metadata for offset-based pagination
     */
    public static function offset(
        PaginationRequest $request,
        int $totalCount,
        int $resultCount
    ): self {
        $totalPages = $request->limit > 0 ? (int) ceil($totalCount / $request->limit) : 1;
        $currentPage = $request->getPage();

        return new self(
            resultCount: $resultCount,
            limit: $request->limit,
            hasMore: ($request->offset + $request->limit) < $totalCount,
            hasPrevious: $request->offset > 0,
            type: PaginationType::OFFSET,
            totalCount: $totalCount,
            totalPages: $totalPages,
            currentPage: $currentPage,
            nextCursor: null,
            previousCursor: null,
            sortField: $request->sortField,
            direction: $request->direction
        );
    }

    /**
     * Create metadata for cursor-based pagination
     */
    public static function cursor(
        PaginationRequest $request,
        int $resultCount,
        ?Cursor $nextCursor = null,
        ?Cursor $previousCursor = null
    ): self {
        return new self(
            resultCount: $resultCount,
            limit: $request->limit,
            hasMore: $nextCursor !== null,
            hasPrevious: $previousCursor !== null,
            type: PaginationType::CURSOR,
            totalCount: null,
            totalPages: null,
            currentPage: null,
            nextCursor: $nextCursor,
            previousCursor: $previousCursor,
            sortField: $request->sortField,
            direction: $request->direction
        );
    }

    /**
     * Check if this is offset-based pagination
     */
    public function isOffsetBased(): bool
    {
        return $this->type->isOffset();
    }

    /**
     * Check if this is cursor-based pagination
     */
    public function isCursorBased(): bool
    {
        return $this->type->isCursor();
    }

    /**
     * Create new metadata with different result count
     */
    public function withResultCount(int $resultCount): self
    {
        return new self(
            resultCount: $resultCount,
            limit: $this->limit,
            hasMore: $this->hasMore,
            hasPrevious: $this->hasPrevious,
            type: $this->type,
            totalCount: $this->totalCount,
            totalPages: $this->totalPages,
            currentPage: $this->currentPage,
            nextCursor: $this->nextCursor,
            previousCursor: $this->previousCursor,
            sortField: $this->sortField,
            direction: $this->direction
        );
    }

    /**
     * Get navigation information for offset-based pagination
     */
    public function getNavigationInfo(): array
    {
        if (! $this->isOffsetBased()) {
            return [];
        }

        return [
            'first_page' => 1,
            'last_page' => $this->totalPages,
            'current_page' => $this->currentPage,
            'next_page' => $this->hasMore ? $this->currentPage + 1 : null,
            'previous_page' => $this->hasPrevious ? $this->currentPage - 1 : null,
        ];
    }

    /**
     * Get cursor navigation information
     */
    public function getCursorInfo(): array
    {
        if (! $this->isCursorBased()) {
            return [];
        }

        return [
            'next_cursor' => $this->nextCursor?->toEncoded(),
            'previous_cursor' => $this->previousCursor?->toEncoded(),
        ];
    }

    /**
     * Convert to array for JSON serialization
     */
    public function toArray(): array
    {
        $base = [
            'result_count' => $this->resultCount,
            'limit' => $this->limit,
            'has_more' => $this->hasMore,
            'has_previous' => $this->hasPrevious,
            'type' => $this->type->value,
            'sort_field' => $this->sortField,
            'direction' => $this->direction->value,
        ];

        if ($this->isOffsetBased()) {
            $base = array_merge($base, [
                'total_count' => $this->totalCount,
                'total_pages' => $this->totalPages,
                'current_page' => $this->currentPage,
            ], $this->getNavigationInfo());
        }

        if ($this->isCursorBased()) {
            $base = array_merge($base, $this->getCursorInfo());
        }

        return $base;
    }

    /**
     * Check if results are at the maximum limit
     */
    public function isAtLimit(): bool
    {
        return $this->resultCount === $this->limit;
    }

    /**
     * Get pagination type
     */
    public function getType(): PaginationType
    {
        return $this->type;
    }
}
