<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Sourcing\Versioning;

final class EventUpcaster
{
    public function __construct(
        private readonly string $upcasterId,
        private readonly string $eventType,
        private readonly int $fromVersion,
        private readonly int $toVersion,
        private readonly array $fieldMap = [],
        private readonly array $defaults = [],
        private readonly string $status = 'active',
        private readonly string $createdAt = ''
    ) {}

    public static function create(string $upcasterId, string $eventType, int $fromVersion, int $toVersion, array $fieldMap = [], array $defaults = []): self
    {
        return new self($upcasterId, $eventType, max(1, $fromVersion), max(1, $toVersion), $fieldMap, $defaults, 'active', date('c'));
    }

    public static function fromArray(array $data): self
    {
        return new self((string) ($data['upcasterId'] ?? ''), (string) ($data['eventType'] ?? ''), (int) ($data['fromVersion'] ?? 1), (int) ($data['toVersion'] ?? 1), (array) ($data['fieldMap'] ?? []), (array) ($data['defaults'] ?? []), (string) ($data['status'] ?? 'active'), (string) ($data['createdAt'] ?? ''));
    }

    public function upcasterId(): string { return $this->upcasterId; }
    public function eventType(): string { return $this->eventType; }
    public function fromVersion(): int { return max(1, $this->fromVersion); }
    public function toVersion(): int { return max(1, $this->toVersion); }
    public function active(): bool { return $this->status === 'active'; }

    public function upcast(array $event): array
    {
        $payload = (array) ($event['payload'] ?? []);
        $newPayload = $payload;

        foreach ($this->fieldMap as $old => $new) {
            if (array_key_exists((string) $old, $payload)) {
                $newPayload[(string) $new] = $payload[(string) $old];
                unset($newPayload[(string) $old]);
            }
        }

        foreach ($this->defaults as $key => $value) {
            if (!array_key_exists((string) $key, $newPayload)) {
                $newPayload[(string) $key] = $value;
            }
        }

        $metadata = (array) ($event['metadata'] ?? []);
        $metadata['eventVersion'] = $this->toVersion();
        $metadata['upcastedFrom'] = $this->fromVersion();
        $metadata['upcasterId'] = $this->upcasterId;

        $event['payload'] = $newPayload;
        $event['metadata'] = $metadata;
        $event['version'] = $this->toVersion();

        return $event;
    }

    public function toArray(): array
    {
        return ['upcasterId' => $this->upcasterId, 'eventType' => $this->eventType, 'fromVersion' => $this->fromVersion(), 'toVersion' => $this->toVersion(), 'fieldMap' => $this->fieldMap, 'defaults' => $this->defaults, 'status' => $this->status, 'active' => $this->active(), 'createdAt' => $this->createdAt !== '' ? $this->createdAt : date('c')];
    }
}
