<?php

declare(strict_types=1);

namespace App\Framework\Http\Session\ValueObjects;

use App\Framework\Core\ValueObjects\Timestamp;

/**
 * Value Object representing form data with metadata
 */
final readonly class FormData
{
    /**
     * @param array<string, mixed> $fields Form field data
     * @param Timestamp $timestamp When the form data was stored
     * @param array<string, mixed> $metadata Additional metadata
     */
    public function __construct(
        public array $fields,
        public Timestamp $timestamp,
        public array $metadata = []
    ) {
    }

    /**
     * Create from raw array
     * @param array<string, mixed> $rawData
     */
    public static function fromArray(array $rawData): self
    {
        // Extract metadata
        $timestamp = isset($rawData['__timestamp'])
            ? Timestamp::fromFloat((float) $rawData['__timestamp'])
            : Timestamp::fromFloat(microtime(true));

        $metadata = $rawData['__metadata'] ?? [];

        // Extract field data (everything that's not metadata)
        $fields = $rawData;
        unset($fields['__timestamp'], $fields['__metadata']);

        return new self(
            fields: $fields,
            timestamp: $timestamp,
            metadata: $metadata
        );
    }

    /**
     * Create new form data with current timestamp
     * @param array<string, mixed> $fields
     * @param array<string, mixed> $metadata
     */
    public static function create(array $fields, array $metadata = []): self
    {
        return new self(
            fields: $fields,
            timestamp: Timestamp::fromFloat(microtime(true)),
            metadata: $metadata
        );
    }

    /**
     * Convert to array
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return array_merge($this->fields, [
            '__timestamp' => $this->timestamp->toFloat(),
            '__metadata' => $this->metadata,
        ]);
    }

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

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

    /**
     * Get all field names
     * @return array<string>
     */
    public function getFieldNames(): array
    {
        return array_keys($this->fields);
    }

    /**
     * Create new instance with updated field
     */
    public function withField(string $field, mixed $value): self
    {
        $fields = $this->fields;
        $fields[$field] = $value;

        return new self(
            fields: $fields,
            timestamp: $this->timestamp,
            metadata: $this->metadata
        );
    }

    /**
     * Create new instance without field
     */
    public function withoutField(string $field): self
    {
        $fields = $this->fields;
        unset($fields[$field]);

        return new self(
            fields: $fields,
            timestamp: $this->timestamp,
            metadata: $this->metadata
        );
    }

    /**
     * Check if form data is empty
     */
    public function isEmpty(): bool
    {
        return empty($this->fields);
    }

    /**
     * Get field count
     */
    public function count(): int
    {
        return count($this->fields);
    }

    /**
     * Get metadata value
     */
    public function getMetadata(string $key, mixed $default = null): mixed
    {
        return $this->metadata[$key] ?? $default;
    }

    /**
     * Check if form data is older than given seconds
     */
    public function isOlderThan(int $seconds): bool
    {
        return $this->timestamp->toFloat() < (time() - $seconds);
    }

    /**
     * Get age in seconds
     */
    public function getAge(): int
    {
        return (int) (time() - $this->timestamp->toFloat());
    }
}
