<?php

namespace Tests\Unit\Framework\Scheduler;

use Openguru\OpenCartFramework\Scheduler\Job;
use Openguru\OpenCartFramework\Scheduler\TaskInterface;
use Tests\TestCase;

class JobTest extends TestCase
{
    public function testJobCreation()
    {
        // Arrange
        $container = $this->app;

        // Act
        $job = new Job($container, function() {}, 'TestJob');

        // Assert
        $this->assertEquals('TestJob', $job->getName());
        $this->assertEquals(md5('TestJob'), $job->getId());
    }

    public function testJobWithoutNameUsesClassName()
    {
        // Arrange
        $container = $this->app;

        // Act
        $job = new Job($container, TestTask::class);

        // Assert
        $this->assertEquals(TestTask::class, $job->getName());
    }

    public function testJobWithClosureGetsClosureName()
    {
        // Arrange
        $container = $this->app;

        // Act
        $job = new Job($container, function() {});

        // Assert
        $this->assertEquals('Closure', $job->getName());
    }

    public function testEveryMinuteSchedule()
    {
        // Arrange
        $container = $this->app;
        $job = new Job($container, function() {});

        // Act
        $job->everyMinute();

        // Assert
        $this->assertEquals('* * * * *', $job->getExpression());
    }

    public function testEveryFiveMinutesSchedule()
    {
        // Arrange
        $container = $this->app;
        $job = new Job($container, function() {});

        // Act
        $job->everyFiveMinutes();

        // Assert
        $this->assertEquals('*/5 * * * *', $job->getExpression());
    }

    public function testEveryHourSchedule()
    {
        // Arrange
        $container = $this->app;
        $job = new Job($container, function() {});

        // Act
        $job->everyHour();

        // Assert
        $this->assertEquals('0 * * * *', $job->getExpression());
    }

    public function testDailyAtSchedule()
    {
        // Arrange
        $container = $this->app;
        $job = new Job($container, function() {});

        // Act
        $job->dailyAt(9, 30);

        // Assert
        $this->assertEquals('30 9 * * *', $job->getExpression());
    }

    public function testAtSchedule()
    {
        // Arrange
        $container = $this->app;
        $job = new Job($container, function() {});

        // Act
        $job->at('0 12 * * 1');

        // Assert
        $this->assertEquals('0 12 * * 1', $job->getExpression());
    }

    public function testIsDueReturnsTrueForMinuteExpression()
    {
        // Arrange
        $container = $this->app;
        $job = new Job($container, function() {});
        $job->everyMinute();

        // Act
        $isDue = $job->isDue();

        // Assert
        $this->assertTrue($isDue);
    }

    public function testIsDueReturnsFalseForFutureExpression()
    {
        // Arrange
        $container = $this->app;
        $job = new Job($container, function() {});
        // Set to run at a specific future time
        $job->at('0 23 * * *'); // 23:00 every day

        // Act
        $isDue = $job->isDue();

        // Assert - depends on current time, but generally should be false unless it's exactly 23:00
        // This test is not deterministic, so we'll just check it's boolean
        $this->assertIsBool($isDue);
    }

    public function testRunExecutesClosure()
    {
        // Arrange
        $container = $this->app;
        $executed = false;

        $job = new Job($container, function() use (&$executed) {
            $executed = true;
        });

        // Act
        $job->run();

        // Assert
        $this->assertTrue($executed);
    }

    public function testRunExecutesTaskClass()
    {
        // Arrange
        $container = $this->app;

        // Mock the task class binding
        $container->singleton(TestTask::class, function() {
            return new TestTask();
        });

        $job = new Job($container, TestTask::class);

        // Act
        $job->run();

        // Assert
        $this->assertTrue(TestTask::$executed);
        TestTask::reset(); // Reset for other tests
    }

    public function testRunExecutesTaskInterface()
    {
        // Arrange
        $container = $this->app;
        $task = new TestTask();
        $job = new Job($container, $task);

        // Act
        $job->run();

        // Assert
        $this->assertTrue(TestTask::$executed);
        TestTask::reset(); // Reset for other tests
    }
}

// Test helper class
class TestTask implements TaskInterface
{
    public static bool $executed = false;

    public function execute(): void
    {
        self::$executed = true;
    }

    public static function reset(): void
    {
        self::$executed = false;
    }
}
