<?php

declare(strict_types=1);

namespace App\Framework\EventSourcing\Replay;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\EventSourcing\AggregateEnvelope;

/**
 * Time Range Replay Strategy
 *
 * Replays events within a specific time range
 */
final readonly class TimeRangeReplayStrategy implements ReplayStrategy
{
    public function __construct(
        private Timestamp $startTime,
        private Timestamp $endTime
    ) {
        if ($startTime->isAfter($endTime)) {
            throw new \InvalidArgumentException('Start time must be before end time');
        }
    }

    public function selectEvents(array $events): array
    {
        return array_filter(
            $events,
            function (AggregateEnvelope $envelope) {
                $eventTime = $envelope->timestamp;

                return $eventTime->isAfter($this->startTime) || $eventTime->equals($this->startTime)
                    && ($eventTime->isBefore($this->endTime) || $eventTime->equals($this->endTime));
            }
        );
    }

    public function getDescription(): string
    {
        return sprintf(
            'Replay events from %s to %s',
            $this->startTime->format('Y-m-d H:i:s'),
            $this->endTime->format('Y-m-d H:i:s')
        );
    }

    /**
     * Factory: Today's events
     */
    public static function today(): self
    {
        $startOfDay = Timestamp::now()->startOfDay();
        $endOfDay = Timestamp::now()->endOfDay();

        return new self($startOfDay, $endOfDay);
    }

    /**
     * Factory: Yesterday's events
     */
    public static function yesterday(): self
    {
        $yesterday = Timestamp::yesterday();
        $startOfDay = $yesterday->startOfDay();
        $endOfDay = $yesterday->endOfDay();

        return new self($startOfDay, $endOfDay);
    }

    /**
     * Factory: Last N hours
     */
    public static function lastHours(int $hours): self
    {
        $endTime = Timestamp::now();
        $startTime = $endTime->subHours($hours);

        return new self($startTime, $endTime);
    }

    /**
     * Factory: Last N days
     */
    public static function lastDays(int $days): self
    {
        $endTime = Timestamp::now();
        $startTime = $endTime->subDays($days);

        return new self($startTime, $endTime);
    }
}
