<?php

declare(strict_types=1);

namespace App\Application\LiveComponents\ImageGallery;

use App\Application\LiveComponents\LiveComponentState;

/**
 * Type-safe state for ImageGalleryComponent
 *
 * Manages image gallery state with filtering, sorting, pagination, and selection.
 */
final readonly class ImageGalleryState implements LiveComponentState
{
    public function __construct(
        public array $images = [],
        public string $sortBy = 'created_at',
        public string $sortDirection = 'desc',
        public ?string $filterMimeType = null,
        public string $searchQuery = '',
        public int $currentPage = 1,
        public int $itemsPerPage = 20,
        public array $selectedImages = [],
        public int $totalImages = 0,
        public bool $hasMore = false,
        public string $dataSource = 'database'
    ) {
    }

    /**
     * Create from ComponentData (Framework → Domain conversion)
     */
    public static function fromArray(array $data): self
    {
        return new self(
            images: ($data['images'] ?? []),
            sortBy: (string) ($data['sort_by'] ?? 'created_at'),
            sortDirection: (string) ($data['sort_direction'] ?? 'desc'),
            filterMimeType: $data['filter_mime_type'],
            searchQuery: (string) ($data['search_query'] ?? ''),
            currentPage: (int) ($data['current_page'] ?? 1),
            itemsPerPage: (int) ($data['items_per_page'] ?? 20),
            selectedImages: ($data['selected_images'] ?? []),
            totalImages: (int) ($data['total_images'] ?? 0),
            hasMore: (bool) ($data['has_more'] ?? false),
            dataSource: (string) ($data['data_source'] ?? 'database')
        );
    }

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

    /**
     * Convert to ComponentData (Domain → Framework conversion)
     */
    public function toArray(): array
    {
        return [
            'images' => $this->images,
            'sort_by' => $this->sortBy,
            'sort_direction' => $this->sortDirection,
            'filter_mime_type' => $this->filterMimeType,
            'search_query' => $this->searchQuery,
            'current_page' => $this->currentPage,
            'items_per_page' => $this->itemsPerPage,
            'selected_images' => $this->selectedImages,
            'total_images' => $this->totalImages,
            'has_more' => $this->hasMore,
            'data_source' => $this->dataSource,
        ];
    }

    /**
     * Update with new sort parameters
     */
    public function withSort(string $sortBy, string $sortDirection): self
    {
        return new self(
            images: $this->images,
            sortBy: $sortBy,
            sortDirection: $sortDirection,
            filterMimeType: $this->filterMimeType,
            searchQuery: $this->searchQuery,
            currentPage: 1, // Reset to first page on sort change
            itemsPerPage: $this->itemsPerPage,
            selectedImages: $this->selectedImages,
            totalImages: $this->totalImages,
            hasMore: $this->hasMore,
            dataSource: $this->dataSource
        );
    }

    /**
     * Update with mime type filter
     */
    public function withMimeTypeFilter(?string $mimeType): self
    {
        return new self(
            images: $this->images,
            sortBy: $this->sortBy,
            sortDirection: $this->sortDirection,
            filterMimeType: $mimeType,
            searchQuery: $this->searchQuery,
            currentPage: 1, // Reset to first page on filter change
            itemsPerPage: $this->itemsPerPage,
            selectedImages: $this->selectedImages,
            totalImages: $this->totalImages,
            hasMore: $this->hasMore,
            dataSource: $this->dataSource
        );
    }

    /**
     * Update with search query
     */
    public function withSearch(string $query): self
    {
        return new self(
            images: $this->images,
            sortBy: $this->sortBy,
            sortDirection: $this->sortDirection,
            filterMimeType: $this->filterMimeType,
            searchQuery: $query,
            currentPage: 1, // Reset to first page on search
            itemsPerPage: $this->itemsPerPage,
            selectedImages: $this->selectedImages,
            totalImages: $this->totalImages,
            hasMore: $this->hasMore,
            dataSource: $this->dataSource
        );
    }

    /**
     * Update with new images (for load more)
     */
    public function withImages(array $images, int $totalImages, bool $hasMore): self
    {
        return new self(
            images: $images,
            sortBy: $this->sortBy,
            sortDirection: $this->sortDirection,
            filterMimeType: $this->filterMimeType,
            searchQuery: $this->searchQuery,
            currentPage: $this->currentPage,
            itemsPerPage: $this->itemsPerPage,
            selectedImages: $this->selectedImages,
            totalImages: $totalImages,
            hasMore: $hasMore,
            dataSource: $this->dataSource
        );
    }

    /**
     * Load more images (increment page)
     */
    public function withNextPage(): self
    {
        return new self(
            images: $this->images,
            sortBy: $this->sortBy,
            sortDirection: $this->sortDirection,
            filterMimeType: $this->filterMimeType,
            searchQuery: $this->searchQuery,
            currentPage: $this->currentPage + 1,
            itemsPerPage: $this->itemsPerPage,
            selectedImages: $this->selectedImages,
            totalImages: $this->totalImages,
            hasMore: $this->hasMore,
            dataSource: $this->dataSource
        );
    }

    /**
     * Toggle image selection
     */
    public function withToggledSelection(string $imageUlid): self
    {
        $selected = $this->selectedImages;

        if (in_array($imageUlid, $selected, true)) {
            // Remove from selection
            $selected = array_values(array_filter($selected, fn ($id) => $id !== $imageUlid));
        } else {
            // Add to selection
            $selected[] = $imageUlid;
        }

        return new self(
            images: $this->images,
            sortBy: $this->sortBy,
            sortDirection: $this->sortDirection,
            filterMimeType: $this->filterMimeType,
            searchQuery: $this->searchQuery,
            currentPage: $this->currentPage,
            itemsPerPage: $this->itemsPerPage,
            selectedImages: $selected,
            totalImages: $this->totalImages,
            hasMore: $this->hasMore,
            dataSource: $this->dataSource
        );
    }

    /**
     * Clear all selections
     */
    public function withClearedSelection(): self
    {
        return new self(
            images: $this->images,
            sortBy: $this->sortBy,
            sortDirection: $this->sortDirection,
            filterMimeType: $this->filterMimeType,
            searchQuery: $this->searchQuery,
            currentPage: $this->currentPage,
            itemsPerPage: $this->itemsPerPage,
            selectedImages: [],
            totalImages: $this->totalImages,
            hasMore: $this->hasMore,
            dataSource: $this->dataSource
        );
    }

    /**
     * Remove image from current list
     */
    public function withoutImage(string $imageUlid): self
    {
        $filteredImages = array_filter($this->images, fn ($img) => $img['ulid'] !== $imageUlid);

        return new self(
            images: array_values($filteredImages),
            sortBy: $this->sortBy,
            sortDirection: $this->sortDirection,
            filterMimeType: $this->filterMimeType,
            searchQuery: $this->searchQuery,
            currentPage: $this->currentPage,
            itemsPerPage: $this->itemsPerPage,
            selectedImages: array_values(array_filter($this->selectedImages, fn ($id) => $id !== $imageUlid)),
            totalImages: max(0, $this->totalImages - 1),
            hasMore: $this->hasMore,
            dataSource: $this->dataSource
        );
    }

    /**
     * Check if has images
     */
    public function hasImages(): bool
    {
        return ! empty($this->images);
    }

    /**
     * Check if image is selected
     */
    public function isSelected(string $imageUlid): bool
    {
        return in_array($imageUlid, $this->selectedImages, true);
    }

    /**
     * Get count of selected images
     */
    public function getSelectionCount(): int
    {
        return count($this->selectedImages);
    }

    /**
     * Get prepared images for display with pre-computed properties
     */
    public function getPreparedImages(): array
    {
        return array_map(function ($image) {
            return [
                'ulid' => $image['ulid'],
                'filename' => $image['filename'],
                'original_filename' => $image['original_filename'] ?? $image['filename'],
                'url' => $image['url'] ?? '/images/' . $image['filename'],
                'thumbnail_url' => $image['thumbnail_url'] ?? '/images/' . $image['filename'],
                'width' => $image['width'] ?? 0,
                'height' => $image['height'] ?? 0,
                'mime_type' => $image['mime_type'] ?? 'image/jpeg',
                'mime_type_label' => $this->getMimeTypeLabel($image['mime_type'] ?? 'image/jpeg'),
                'file_size' => $image['file_size'] ?? 0,
                'file_size_formatted' => $this->formatFileSize($image['file_size'] ?? 0),
                'alt_text' => $image['alt_text'] ?? '',
                'created_at' => $image['created_at'] ?? '',
                'created_at_formatted' => $this->formatDate($image['created_at'] ?? ''),
                'dimensions' => ($image['width'] ?? 0) . ' × ' . ($image['height'] ?? 0),
                'is_selected' => $this->isSelected($image['ulid']),
                'selected_class' => $this->isSelected($image['ulid']) ? 'gallery__item--selected' : '',
            ];
        }, $this->images);
    }

    /**
     * Get available mime type filters
     */
    public function getMimeTypeOptions(): array
    {
        $options = [
            '' => 'All Types',
            'image/jpeg' => 'JPEG',
            'image/png' => 'PNG',
            'image/gif' => 'GIF',
            'image/webp' => 'WebP',
        ];

        return array_map(function ($value, $label) {
            $isSelected = $value === ($this->filterMimeType ?? '');

            return [
                'value' => $value,
                'label' => $label,
                'selected' => $isSelected ? 'selected' : '',
            ];
        }, array_keys($options), array_values($options));
    }

    /**
     * Get sort options
     */
    public function getSortOptions(): array
    {
        $options = [
            'created_at' => 'Upload Date',
            'filename' => 'Filename',
            'file_size' => 'File Size',
            'width' => 'Width',
            'height' => 'Height',
        ];

        return array_map(function ($value, $label) {
            $isSelected = $value === $this->sortBy;

            return [
                'value' => $value,
                'label' => $label,
                'selected' => $isSelected ? 'selected' : '',
            ];
        }, array_keys($options), array_values($options));
    }

    /**
     * Get pagination info
     */
    public function getPaginationInfo(): array
    {
        $displayedCount = count($this->images);

        return [
            'current_page' => $this->currentPage,
            'items_per_page' => $this->itemsPerPage,
            'displayed_count' => $displayedCount,
            'total_count' => $this->totalImages,
            'has_more' => $this->hasMore,
            'showing_text' => "Showing {$displayedCount} of {$this->totalImages} images",
        ];
    }

    /**
     * Format file size
     */
    private function formatFileSize(int $bytes): string
    {
        $units = ['B', 'KB', 'MB', 'GB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        $bytes /= (1 << (10 * $pow));

        return round($bytes, 2) . ' ' . $units[$pow];
    }

    /**
     * Format date
     */
    private function formatDate(string $date): string
    {
        if (empty($date)) {
            return 'Unknown';
        }

        try {
            $dt = new \DateTimeImmutable($date);

            return $dt->format('d.m.Y H:i');
        } catch (\Exception $e) {
            return $date;
        }
    }

    /**
     * Get mime type label
     */
    private function getMimeTypeLabel(string $mimeType): string
    {
        return match ($mimeType) {
            'image/jpeg' => 'JPEG',
            'image/png' => 'PNG',
            'image/gif' => 'GIF',
            'image/webp' => 'WebP',
            'image/svg+xml' => 'SVG',
            default => strtoupper(str_replace('image/', '', $mimeType))
        };
    }
}
