<?php

declare(strict_types=1);

namespace App\Application\LiveComponents\ProductFilter;

use App\Application\LiveComponents\LiveComponentState;

/**
 * Type-safe state for ProductFilterComponent
 *
 * Provides compile-time type safety for product filtering functionality.
 * All properties are readonly and immutable - transformations return new instances.
 */
final readonly class ProductFilterState implements LiveComponentState
{
    public function __construct(
        public array $activeFilters = [],
        public array $availableCategories = [],
        public array $availableBrands = [],
        public array $availableColors = [],
        public array $availableSizes = [],
        public array $priceRange = ['min' => 0, 'max' => 1000],
        public string $sortBy = 'relevance'
    ) {
    }

    /**
     * Create from ComponentData (Framework → Domain conversion)
     */
    public static function fromArray(array $data): self
    {
        return new self(
            activeFilters: ($data['active_filters'] ?? []),
            availableCategories: ($data['available_categories'] ?? []),
            availableBrands: ($data['available_brands'] ?? []),
            availableColors: ($data['available_colors'] ?? []),
            availableSizes: ($data['available_sizes'] ?? []),
            priceRange: ($data['price_range'] ?? ['min' => 0, 'max' => 1000]),
            sortBy: (string) ($data['sort_by'] ?? 'relevance')
        );
    }

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

    /**
     * Convert to ComponentData (Domain → Framework conversion)
     */
    public function toArray(): array
    {
        return [
            'active_filters' => $this->activeFilters,
            'available_categories' => $this->availableCategories,
            'available_brands' => $this->availableBrands,
            'available_colors' => $this->availableColors,
            'available_sizes' => $this->availableSizes,
            'price_range' => $this->priceRange,
            'sort_by' => $this->sortBy,
        ];
    }

    /**
     * Set category filter
     */
    public function withCategory(string $category): self
    {
        $activeFilters = $this->activeFilters;
        $activeFilters['category'] = $category;

        return new self(
            activeFilters: $activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $this->sortBy
        );
    }

    /**
     * Set price range filter
     */
    public function withPriceRange(float $min, float $max): self
    {
        $activeFilters = $this->activeFilters;
        $activeFilters['price_min'] = $min;
        $activeFilters['price_max'] = $max;

        return new self(
            activeFilters: $activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $this->sortBy
        );
    }

    /**
     * Toggle brand filter
     */
    public function withToggledBrand(string $brand): self
    {
        $activeFilters = $this->activeFilters;
        $brands = $activeFilters['brands'] ?? [];

        if (in_array($brand, $brands)) {
            $brands = array_filter($brands, fn ($b) => $b !== $brand);
        } else {
            $brands[] = $brand;
        }

        $activeFilters['brands'] = array_values($brands);

        return new self(
            activeFilters: $activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $this->sortBy
        );
    }

    /**
     * Toggle color filter
     */
    public function withToggledColor(string $color): self
    {
        $activeFilters = $this->activeFilters;
        $colors = $activeFilters['colors'] ?? [];

        if (in_array($color, $colors)) {
            $colors = array_filter($colors, fn ($c) => $c !== $color);
        } else {
            $colors[] = $color;
        }

        $activeFilters['colors'] = array_values($colors);

        return new self(
            activeFilters: $activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $this->sortBy
        );
    }

    /**
     * Toggle size filter
     */
    public function withToggledSize(string $size): self
    {
        $activeFilters = $this->activeFilters;
        $sizes = $activeFilters['sizes'] ?? [];

        if (in_array($size, $sizes)) {
            $sizes = array_filter($sizes, fn ($s) => $s !== $size);
        } else {
            $sizes[] = $size;
        }

        $activeFilters['sizes'] = array_values($sizes);

        return new self(
            activeFilters: $activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $this->sortBy
        );
    }

    /**
     * Set minimum rating filter
     */
    public function withMinRating(int $rating): self
    {
        $activeFilters = $this->activeFilters;
        $activeFilters['min_rating'] = $rating;

        return new self(
            activeFilters: $activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $this->sortBy
        );
    }

    /**
     * Toggle in-stock-only filter
     */
    public function withToggledInStock(): self
    {
        $activeFilters = $this->activeFilters;
        $activeFilters['in_stock_only'] = ! ($activeFilters['in_stock_only'] ?? false);

        return new self(
            activeFilters: $activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $this->sortBy
        );
    }

    /**
     * Change sort order
     */
    public function withSortBy(string $sortBy): self
    {
        return new self(
            activeFilters: $this->activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $sortBy
        );
    }

    /**
     * Clear all filters
     */
    public function withClearedFilters(): self
    {
        return new self(
            activeFilters: [],
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: 'relevance'
        );
    }

    /**
     * Remove specific filter
     */
    public function withRemovedFilter(string $filterType, ?string $value = null): self
    {
        $activeFilters = $this->activeFilters;

        if ($filterType === 'category') {
            unset($activeFilters['category']);
        } elseif ($filterType === 'price') {
            unset($activeFilters['price_min']);
            unset($activeFilters['price_max']);
        } elseif ($filterType === 'brand' && $value) {
            $brands = $activeFilters['brands'] ?? [];
            $activeFilters['brands'] = array_values(array_filter($brands, fn ($b) => $b !== $value));
        } elseif ($filterType === 'color' && $value) {
            $colors = $activeFilters['colors'] ?? [];
            $activeFilters['colors'] = array_values(array_filter($colors, fn ($c) => $c !== $value));
        } elseif ($filterType === 'size' && $value) {
            $sizes = $activeFilters['sizes'] ?? [];
            $activeFilters['sizes'] = array_values(array_filter($sizes, fn ($s) => $s !== $value));
        } elseif ($filterType === 'rating') {
            unset($activeFilters['min_rating']);
        } elseif ($filterType === 'stock') {
            $activeFilters['in_stock_only'] = false;
        }

        return new self(
            activeFilters: $activeFilters,
            availableCategories: $this->availableCategories,
            availableBrands: $this->availableBrands,
            availableColors: $this->availableColors,
            availableSizes: $this->availableSizes,
            priceRange: $this->priceRange,
            sortBy: $this->sortBy
        );
    }

    /**
     * Check if any filters are active
     */
    public function hasActiveFilters(): bool
    {
        return $this->countActiveFilters() > 0;
    }

    /**
     * Count active filters
     */
    public function countActiveFilters(): int
    {
        $count = 0;

        if (isset($this->activeFilters['category'])) {
            $count++;
        }
        if (isset($this->activeFilters['price_min']) || isset($this->activeFilters['price_max'])) {
            $count++;
        }
        $count += count($this->activeFilters['brands'] ?? []);
        $count += count($this->activeFilters['colors'] ?? []);
        $count += count($this->activeFilters['sizes'] ?? []);
        if (isset($this->activeFilters['min_rating'])) {
            $count++;
        }
        if ($this->activeFilters['in_stock_only'] ?? false) {
            $count++;
        }

        return $count;
    }

    /**
     * Check if has category filter
     */
    public function hasCategory(): bool
    {
        return isset($this->activeFilters['category']);
    }

    /**
     * Check if has price filter
     */
    public function hasPriceFilter(): bool
    {
        return isset($this->activeFilters['price_min']) || isset($this->activeFilters['price_max']);
    }

    /**
     * Check if has brand filters
     */
    public function hasBrands(): bool
    {
        return ! empty($this->activeFilters['brands'] ?? []);
    }

    /**
     * Check if has color filters
     */
    public function hasColors(): bool
    {
        return ! empty($this->activeFilters['colors'] ?? []);
    }

    /**
     * Check if has size filters
     */
    public function hasSizes(): bool
    {
        return ! empty($this->activeFilters['sizes'] ?? []);
    }

    /**
     * Check if has rating filter
     */
    public function hasRating(): bool
    {
        return isset($this->activeFilters['min_rating']);
    }

    /**
     * Check if in-stock-only is active
     */
    public function isInStockOnly(): bool
    {
        return $this->activeFilters['in_stock_only'] ?? false;
    }
}
