<?php

declare(strict_types=1);

use App\Application\LiveComponents\Dashboard\SchedulerTimelineComponent;
use App\Application\LiveComponents\Dashboard\SchedulerState;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\Scheduler\Services\SchedulerService;
use App\Framework\Scheduler\ValueObjects\ScheduledTask;
use App\Framework\Scheduler\Schedules\CronSchedule;
use App\Framework\Scheduler\Schedules\IntervalSchedule;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Core\ValueObjects\Duration;

describe('SchedulerTimelineComponent', function () {
    beforeEach(function () {
        $this->scheduler = Mockery::mock(SchedulerService::class);
        $this->componentId = ComponentId::create('scheduler-timeline', 'test');
        $this->initialState = SchedulerState::empty();
    });

    afterEach(function () {
        Mockery::close();
    });

    it('polls and updates state with scheduler data', function () {
        $now = Timestamp::now();
        $in30Minutes = $now->add(Duration::fromMinutes(30));
        $in2Hours = $now->add(Duration::fromHours(2));

        $task1 = new ScheduledTask(
            id: 'backup-task',
            schedule: CronSchedule::fromExpression('0 2 * * *'),
            callback: fn() => true,
            nextRun: $in30Minutes
        );

        $task2 = new ScheduledTask(
            id: 'cleanup-task',
            schedule: IntervalSchedule::every(Duration::fromHours(1)),
            callback: fn() => true,
            nextRun: $in2Hours
        );

        $this->scheduler->shouldReceive('getScheduledTasks')
            ->once()
            ->andReturn([$task1, $task2]);

        $this->scheduler->shouldReceive('getDueTasks')
            ->once()
            ->with(Mockery::type(Timestamp::class))
            ->andReturn([]);

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        $newState = $component->poll();

        expect($newState)->toBeInstanceOf(SchedulerState::class);
        expect($newState->totalScheduledTasks)->toBe(2);
        expect($newState->dueTasks)->toBe(0);
        expect($newState->upcomingTasks)->toHaveCount(2);
        expect($newState->nextExecution)->not->toBeNull();
    });

    it('identifies due tasks correctly', function () {
        $now = Timestamp::now();
        $past = $now->sub(Duration::fromMinutes(5));

        $dueTask = new ScheduledTask(
            id: 'due-task',
            schedule: IntervalSchedule::every(Duration::fromMinutes(10)),
            callback: fn() => true,
            nextRun: $past
        );

        $this->scheduler->shouldReceive('getScheduledTasks')
            ->once()
            ->andReturn([$dueTask]);

        $this->scheduler->shouldReceive('getDueTasks')
            ->once()
            ->andReturn([$dueTask]);

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        $newState = $component->poll();

        expect($newState->dueTasks)->toBe(1);
        expect($newState->upcomingTasks[0]['is_due'])->toBeTrue();
    });

    it('formats time until execution correctly for less than 1 minute', function () {
        $now = Timestamp::now();
        $in30Seconds = $now->add(Duration::fromSeconds(30));

        $task = new ScheduledTask(
            id: 'imminent-task',
            schedule: IntervalSchedule::every(Duration::fromMinutes(1)),
            callback: fn() => true,
            nextRun: $in30Seconds
        );

        $this->scheduler->shouldReceive('getScheduledTasks')
            ->once()
            ->andReturn([$task]);

        $this->scheduler->shouldReceive('getDueTasks')
            ->once()
            ->andReturn([]);

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        $newState = $component->poll();

        expect($newState->upcomingTasks[0]['next_run_relative'])->toBe('Less than 1 minute');
    });

    it('formats time until execution correctly for hours and minutes', function () {
        $now = Timestamp::now();
        $in5Hours30Min = $now->add(Duration::fromMinutes(330)); // 5.5 hours

        $task = new ScheduledTask(
            id: 'future-task',
            schedule: CronSchedule::fromExpression('30 17 * * *'),
            callback: fn() => true,
            nextRun: $in5Hours30Min
        );

        $this->scheduler->shouldReceive('getScheduledTasks')
            ->once()
            ->andReturn([$task]);

        $this->scheduler->shouldReceive('getDueTasks')
            ->once()
            ->andReturn([]);

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        $newState = $component->poll();

        $relativeTime = $newState->upcomingTasks[0]['next_run_relative'];
        expect($relativeTime)->toContain('5 hours');
        expect($relativeTime)->toContain('30 min');
    });

    it('formats time until execution correctly for days and hours', function () {
        $now = Timestamp::now();
        $in2Days4Hours = $now->add(Duration::fromHours(52)); // 2 days, 4 hours

        $task = new ScheduledTask(
            id: 'distant-task',
            schedule: CronSchedule::fromExpression('0 0 * * 0'), // Weekly
            callback: fn() => true,
            nextRun: $in2Days4Hours
        );

        $this->scheduler->shouldReceive('getScheduledTasks')
            ->once()
            ->andReturn([$task]);

        $this->scheduler->shouldReceive('getDueTasks')
            ->once()
            ->andReturn([]);

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        $newState = $component->poll();

        $relativeTime = $newState->upcomingTasks[0]['next_run_relative'];
        expect($relativeTime)->toContain('2 days');
        expect($relativeTime)->toContain('4 hours');
    });

    it('has correct poll interval', function () {
        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        expect($component->getPollInterval())->toBe(30000);
    });

    it('returns correct render data', function () {
        $upcomingTasks = [
            [
                'id' => 'task-1',
                'schedule_type' => 'cron',
                'next_run' => '2024-01-15 13:00:00',
                'next_run_relative' => '30 min',
                'is_due' => false,
            ],
        ];

        $state = new SchedulerState(
            totalScheduledTasks: 5,
            dueTasks: 1,
            upcomingTasks: $upcomingTasks,
            nextExecution: '2024-01-15 13:00:00',
            statistics: ['total_executions' => 100],
            lastUpdated: '2024-01-15 12:00:00'
        );

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $state,
            scheduler: $this->scheduler
        );

        $renderData = $component->getRenderData();

        expect($renderData->templatePath)->toBe('livecomponent-scheduler-timeline');
        expect($renderData->data)->toHaveKey('componentId');
        expect($renderData->data)->toHaveKey('pollInterval');
        expect($renderData->data['pollInterval'])->toBe(30000);
        expect($renderData->data['totalScheduledTasks'])->toBe(5);
        expect($renderData->data['dueTasks'])->toBe(1);
    });

    it('handles no scheduled tasks', function () {
        $this->scheduler->shouldReceive('getScheduledTasks')
            ->once()
            ->andReturn([]);

        $this->scheduler->shouldReceive('getDueTasks')
            ->once()
            ->andReturn([]);

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        $newState = $component->poll();

        expect($newState->totalScheduledTasks)->toBe(0);
        expect($newState->dueTasks)->toBe(0);
        expect($newState->upcomingTasks)->toBe([]);
        expect($newState->nextExecution)->toBeNull();
    });

    it('limits upcoming tasks to 10 items', function () {
        $tasks = [];
        $now = Timestamp::now();

        for ($i = 0; $i < 20; $i++) {
            $tasks[] = new ScheduledTask(
                id: "task-{$i}",
                schedule: IntervalSchedule::every(Duration::fromMinutes($i + 1)),
                callback: fn() => true,
                nextRun: $now->add(Duration::fromMinutes($i + 1))
            );
        }

        $this->scheduler->shouldReceive('getScheduledTasks')
            ->once()
            ->andReturn($tasks);

        $this->scheduler->shouldReceive('getDueTasks')
            ->once()
            ->andReturn([]);

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        $newState = $component->poll();

        expect($newState->totalScheduledTasks)->toBe(20);
        expect($newState->upcomingTasks)->toHaveCount(10); // Limited to 10
    });

    it('detects schedule type correctly', function () {
        $now = Timestamp::now();

        $cronTask = new ScheduledTask(
            id: 'cron-task',
            schedule: CronSchedule::fromExpression('0 * * * *'),
            callback: fn() => true,
            nextRun: $now->add(Duration::fromHours(1))
        );

        $intervalTask = new ScheduledTask(
            id: 'interval-task',
            schedule: IntervalSchedule::every(Duration::fromMinutes(30)),
            callback: fn() => true,
            nextRun: $now->add(Duration::fromMinutes(30))
        );

        $this->scheduler->shouldReceive('getScheduledTasks')
            ->once()
            ->andReturn([$cronTask, $intervalTask]);

        $this->scheduler->shouldReceive('getDueTasks')
            ->once()
            ->andReturn([]);

        $component = new SchedulerTimelineComponent(
            id: $this->componentId,
            state: $this->initialState,
            scheduler: $this->scheduler
        );

        $newState = $component->poll();

        expect($newState->upcomingTasks[0]['schedule_type'])->toBe('cron');
        expect($newState->upcomingTasks[1]['schedule_type'])->toBe('interval');
    });
});
