<?php

declare(strict_types=1);

namespace App\Application\LiveComponents\DataTable;

use App\Application\LiveComponents\LiveComponentState;

/**
 * Type-safe state for DataTableComponent
 *
 * Provides compile-time type safety for data table functionality.
 * All properties are readonly and immutable - transformations return new instances.
 */
final readonly class DataTableState implements LiveComponentState
{
    public function __construct(
        public array $rows = [],
        public array $sort = ['column' => 'id', 'direction' => 'asc'],
        public int $page = 1,
        public int $pageSize = 10,
        public int $totalRows = 0,
        public int $totalPages = 1,
        public array $selectedRows = [],
        public array $filters = [],
        public ?string $message = null
    ) {
    }

    /**
     * Create from ComponentData (Framework → Domain conversion)
     */
    public static function fromArray(array $data): self
    {
        return new self(
            rows: ($data['rows'] ?? []),
            sort: ($data['sort'] ?? ['column' => 'id', 'direction' => 'asc']),
            page: (int) ($data['page'] ?? 1),
            pageSize: (int) ($data['page_size'] ?? 10),
            totalRows: (int) ($data['total_rows'] ?? 0),
            totalPages: (int) ($data['total_pages'] ?? 1),
            selectedRows: ($data['selected_rows'] ?? []),
            filters: ($data['filters'] ?? []),
            message: ($data['message'] ?? null)
        );
    }

    /**
     * Create empty state with defaults
     */
    public static function empty(): self
    {
        return new self();
    }

    /**
     * Create state with smart defaults for initial component creation
     *
     * Only requires the essential parameters that should be set initially.
     * All other values use sensible defaults.
     */
    public static function withDefaults(
        array $rows = [],
        int $page = 1,
        int $pageSize = 10
    ): self {
        $totalRows = count($rows);
        $totalPages = $pageSize > 0 ? (int) ceil($totalRows / $pageSize) : 1;

        return new self(
            rows: $rows,
            sort: ['column' => 'id', 'direction' => 'asc'],
            page: $page,
            pageSize: $pageSize,
            totalRows: $totalRows,
            totalPages: $totalPages,
            selectedRows: [],
            filters: [
                'id' => '',
                'name' => '',
                'email' => '',
                'role' => '',
                'status' => '',
            ],
            message: null
        );
    }

    /**
     * Convert to ComponentData (Domain → Framework conversion)
     */
    public function toArray(): array
    {
        return [
            'rows' => $this->rows,
            'sort' => $this->sort,
            'page' => $this->page,
            'page_size' => $this->pageSize,
            'total_rows' => $this->totalRows,
            'total_pages' => $this->totalPages,
            'selected_rows' => $this->selectedRows,
            'filters' => $this->filters,
            'message' => $this->message,
        ];
    }

    /**
     * Update with sorted data
     */
    public function withSort(array $rows, string $column, string $direction, int $totalRows, int $totalPages): self
    {
        return new self(
            rows: $rows,
            sort: ['column' => $column, 'direction' => $direction],
            page: $this->page,
            pageSize: $this->pageSize,
            totalRows: $totalRows,
            totalPages: $totalPages,
            selectedRows: $this->selectedRows,
            filters: $this->filters,
            message: $this->message
        );
    }

    /**
     * Change page
     */
    public function withPage(array $rows, int $page, int $totalRows, int $totalPages): self
    {
        return new self(
            rows: $rows,
            sort: $this->sort,
            page: $page,
            pageSize: $this->pageSize,
            totalRows: $totalRows,
            totalPages: $totalPages,
            selectedRows: $this->selectedRows,
            filters: $this->filters,
            message: $this->message
        );
    }

    /**
     * Change page size
     */
    public function withPageSize(array $rows, int $pageSize, int $totalRows, int $totalPages): self
    {
        return new self(
            rows: $rows,
            sort: $this->sort,
            page: 1,
            pageSize: $pageSize,
            totalRows: $totalRows,
            totalPages: $totalPages,
            selectedRows: [],
            filters: $this->filters,
            message: $this->message
        );
    }

    /**
     * Toggle row selection
     */
    public function withToggledRow(int $rowId): self
    {
        $selectedRows = $this->selectedRows;

        if (in_array($rowId, $selectedRows)) {
            $selectedRows = array_values(array_diff($selectedRows, [$rowId]));
        } else {
            $selectedRows[] = $rowId;
        }

        return new self(
            rows: $this->rows,
            sort: $this->sort,
            page: $this->page,
            pageSize: $this->pageSize,
            totalRows: $this->totalRows,
            totalPages: $this->totalPages,
            selectedRows: $selectedRows,
            filters: $this->filters,
            message: $this->message
        );
    }

    /**
     * Select all rows
     */
    public function withAllRowsSelected(array $allRowIds): self
    {
        return new self(
            rows: $this->rows,
            sort: $this->sort,
            page: $this->page,
            pageSize: $this->pageSize,
            totalRows: $this->totalRows,
            totalPages: $this->totalPages,
            selectedRows: $allRowIds,
            filters: $this->filters,
            message: $this->message
        );
    }

    /**
     * Deselect all rows
     */
    public function withNoRowsSelected(): self
    {
        return new self(
            rows: $this->rows,
            sort: $this->sort,
            page: $this->page,
            pageSize: $this->pageSize,
            totalRows: $this->totalRows,
            totalPages: $this->totalPages,
            selectedRows: [],
            filters: $this->filters,
            message: $this->message
        );
    }

    /**
     * Update with filtered data
     */
    public function withFilter(array $rows, array $filters, int $totalRows, int $totalPages): self
    {
        return new self(
            rows: $rows,
            sort: $this->sort,
            page: 1,
            pageSize: $this->pageSize,
            totalRows: $totalRows,
            totalPages: $totalPages,
            selectedRows: [],
            filters: $filters,
            message: $this->message
        );
    }

    /**
     * Update with message
     */
    public function withMessage(string $message): self
    {
        return new self(
            rows: $this->rows,
            sort: $this->sort,
            page: $this->page,
            pageSize: $this->pageSize,
            totalRows: $this->totalRows,
            totalPages: $this->totalPages,
            selectedRows: $this->selectedRows,
            filters: $this->filters,
            message: $message
        );
    }

    /**
     * Clear message
     */
    public function withoutMessage(): self
    {
        return new self(
            rows: $this->rows,
            sort: $this->sort,
            page: $this->page,
            pageSize: $this->pageSize,
            totalRows: $this->totalRows,
            totalPages: $this->totalPages,
            selectedRows: $this->selectedRows,
            filters: $this->filters,
            message: null
        );
    }

    /**
     * Check if there are rows
     */
    public function hasRows(): bool
    {
        return ! empty($this->rows);
    }

    /**
     * Check if there are selected rows
     */
    public function hasSelectedRows(): bool
    {
        return ! empty($this->selectedRows);
    }

    /**
     * Get selected count
     */
    public function getSelectedCount(): int
    {
        return count($this->selectedRows);
    }

    /**
     * Check if can go to next page
     */
    public function canGoNext(): bool
    {
        return $this->page < $this->totalPages;
    }

    /**
     * Check if can go to previous page
     */
    public function canGoPrevious(): bool
    {
        return $this->page > 1;
    }

    /**
     * Check if is first page
     */
    public function isFirstPage(): bool
    {
        return $this->page === 1;
    }

    /**
     * Check if is last page
     */
    public function isLastPage(): bool
    {
        return $this->page === $this->totalPages;
    }

    /**
     * Get normalized filters with defaults
     */
    public function getNormalizedFilters(): array
    {
        return array_merge([
            'id' => '',
            'name' => '',
            'email' => '',
            'role' => '',
            'status' => '',
        ], $this->filters);
    }

}
