<?php

declare(strict_types=1);

namespace App\Framework\Search\ValueObjects;

use InvalidArgumentException;

/**
 * Value Object for search document data
 * Encapsulates the searchable content and attributes of a document
 */
final readonly class DocumentData
{
    /**
     * @param array<string, mixed> $fields
     */
    public function __construct(private array $fields = [])
    {
        $this->validateFields($fields);
    }

    public static function empty(): self
    {
        return new self([]);
    }

    /**
     * @param array<string, mixed> $fields
     */
    public static function fromArray(array $fields): self
    {
        return new self($fields);
    }

    public function with(string $field, mixed $value): self
    {
        $fields = $this->fields;
        $fields[$field] = $value;

        return new self($fields);
    }

    public function without(string $field): self
    {
        $fields = $this->fields;
        unset($fields[$field]);

        return new self($fields);
    }

    public function has(string $field): bool
    {
        return array_key_exists($field, $this->fields);
    }

    public function get(string $field, mixed $default = null): mixed
    {
        return $this->fields[$field] ?? $default;
    }

    /**
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return $this->fields;
    }

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

    /**
     * Get only the text fields for full-text search
     * @return array<string, string>
     */
    public function getTextFields(): array
    {
        return array_filter(
            $this->fields,
            fn ($value) => is_string($value) && ! empty(trim($value))
        );
    }

    /**
     * Get fields suitable for faceting/filtering
     * @return array<string, scalar>
     */
    public function getFacetFields(): array
    {
        return array_filter(
            $this->fields,
            fn ($value) => is_scalar($value)
        );
    }

    /**
     * @param array<string, mixed> $fields
     */
    private function validateFields(array $fields): void
    {
        foreach ($fields as $key => $value) {
            if (! is_string($key) || empty(trim($key))) {
                throw new InvalidArgumentException('Document field keys must be non-empty strings');
            }

            if (strlen($key) > 255) {
                throw new InvalidArgumentException('Document field key cannot exceed 255 characters');
            }

            // Ensure field names are search-engine safe
            if (! preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $key)) {
                throw new InvalidArgumentException('Document field key must start with letter and contain only alphanumeric characters and underscores');
            }

            // Validate value types (only allow serializable data)
            if (is_resource($value) || (is_object($value) && ! method_exists($value, '__toString'))) {
                throw new InvalidArgumentException("Document field '{$key}' contains non-serializable data");
            }
        }
    }
}
