<?php

declare(strict_types=1);

namespace App\Framework\Console\ValueObjects;

use Countable;
use IteratorAggregate;

/**
 * Collection of choice options for multi-select operations
 *
 * @implements IteratorAggregate<int, ChoiceOption>
 */
final readonly class ChoiceOptions implements IteratorAggregate, Countable
{
    /** @var ChoiceOption[] */
    private array $options;

    /**
     * @param ChoiceOption[] $options
     */
    public function __construct(array $options = [])
    {
        $this->options = array_values($options);
    }

    /**
     * Create from legacy array format
     * @param array<string|int, string> $legacyArray
     */
    public static function fromArray(array $legacyArray): self
    {
        $options = [];
        foreach ($legacyArray as $key => $value) {
            $options[] = ChoiceOption::create((string) $key, $value);
        }

        return new self($options);
    }

    public static function create(): self
    {
        return new self();
    }

    public function add(ChoiceOption $option): self
    {
        return new self([...$this->options, $option]);
    }

    public function addChoice(string $value, string $label, bool $selected = false): self
    {
        return $this->add(new ChoiceOption($value, $label, $selected));
    }

    public function selectByValue(string $value): self
    {
        $options = [];
        foreach ($this->options as $option) {
            $options[] = $option->value === $value ? $option->select() : $option;
        }

        return new self($options);
    }

    public function selectByValues(array $values): self
    {
        $options = [];
        foreach ($this->options as $option) {
            $options[] = in_array($option->value, $values) ? $option->select() : $option;
        }

        return new self($options);
    }

    public function deselectAll(): self
    {
        $options = [];
        foreach ($this->options as $option) {
            $options[] = $option->deselect();
        }

        return new self($options);
    }

    public function isEmpty(): bool
    {
        return empty($this->options);
    }

    public function count(): int
    {
        return count($this->options);
    }

    /**
     * @return \Iterator<int, ChoiceOption>
     */
    public function getIterator(): \Iterator
    {
        return new \ArrayIterator($this->options);
    }

    /**
     * Get all options as array
     * @return ChoiceOption[]
     */
    public function toArray(): array
    {
        return $this->options;
    }

    /**
     * Get only selected options
     * @return ChoiceOption[]
     */
    public function getSelected(): array
    {
        return array_filter($this->options, fn (ChoiceOption $option) => $option->isSelected());
    }

    /**
     * Get selected values
     * @return string[]
     */
    public function getSelectedValues(): array
    {
        return array_map(
            fn (ChoiceOption $option) => $option->value,
            $this->getSelected()
        );
    }

    /**
     * Find option by value
     */
    public function findByValue(string $value): ?ChoiceOption
    {
        foreach ($this->options as $option) {
            if ($option->value === $value) {
                return $option;
            }
        }

        return null;
    }

    /**
     * Check if value exists
     */
    public function hasValue(string $value): bool
    {
        return $this->findByValue($value) !== null;
    }

    /**
     * Convert to legacy array format for backward compatibility
     * @return array<string, string>
     */
    public function toLegacyArray(): array
    {
        $legacy = [];
        foreach ($this->options as $option) {
            $legacy[$option->value] = $option->label;
        }

        return $legacy;
    }
}
