<?php

declare(strict_types=1);

/**
 * This file is part of CodeIgniter Tasks.
 *
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

use CodeIgniter\Tasks\Task;
use CodeIgniter\Tasks\TaskRunner;
use CodeIgniter\Test\CIUnitTestCase as TestCase;
use CodeIgniter\Test\DatabaseTestTrait;
use Config\Services;

/**
 * @internal
 */
final class TaskRunnerTest extends TestCase
{
    use DatabaseTestTrait;

    protected $refresh = true;
    protected $namespace;

    protected function setUp(): void
    {
        parent::setUp();

        helper('setting');
        setting('Tasks.logPerformance', true);
    }

    public function testRunWithNoTasks()
    {
        $this->assertNull($this->getRunner()->run());
    }

    public function testRunWithSuccess()
    {
        $task1 = (new Task('closure', static function () {
            echo 'Task 1';
        }))->daily('12:05am')->named('task1');
        $task2 = (new Task('closure', static function () {
            echo 'Task 2';
        }))->daily('12:00am')->named('task2');

        $runner = $this->getRunner([$task1, $task2]);

        ob_start();
        $runner->withTestTime('12:00am')->run();
        $output = ob_get_clean();

        // Only task 2 should have run
        $this->assertSame('Task 2', $output);

        // Should have logged the stats
        $expected = [
            [
                'task'     => 'task2',
                'type'     => 'closure',
                'start'    => date('Y-m-d H:i:s'),
                'duration' => '0.00',
                'output'   => null,
                'error'    => null,
            ],
        ];
        $this->seeInDatabase('settings', [
            'class' => 'CodeIgniter\Tasks\Config\Tasks',
            'key'   => 'log-task2',
            'value' => serialize($expected),
        ]);
        $this->dontSeeInDatabase('settings', [
            'key' => 'log-task1',
        ]);
    }

    /**
     * @throws ReflectionException
     */
    public function testRunWithError()
    {
        $task1 = (new Task('closure', static function () {
            echo 'Task 1';
        }))->daily('12:05am')->named('task1');
        $task3 = (new Task('closure', static function () {
            throw new Exception('Example exception in Task 3');
        }))->daily('12:00am')->named('task3');

        $runner = $this->getRunner([$task1, $task3]);

        ob_start();
        $runner->withTestTime('12:00am')->run();
        $output = ob_get_clean();

        // Only task 3 should have run
        $this->assertSame('', $output);

        // Get info about the exception
        $reflection = new ReflectionFunction($task3->getAction());
        $file       = $reflection->getFileName();
        $line       = $reflection->getStartLine() + 1;

        // Should have logged the stats
        $expected = [
            [
                'task'     => 'task3',
                'type'     => 'closure',
                'start'    => date('Y-m-d H:i:s'),
                'duration' => '0.00',
                'output'   => null,
                'error'    => "Exception: 0 - Example exception in Task 3\nfile: {$file}:{$line}",
            ],
        ];
        $this->seeInDatabase('settings', [
            'class' => 'CodeIgniter\Tasks\Config\Tasks',
            'key'   => 'log-task3',
            'value' => serialize($expected),
        ]);
        $this->dontSeeInDatabase('settings', [
            'key' => 'log-task1',
        ]);
    }

    protected function getRunner(array $tasks = [])
    {
        $scheduler = service('scheduler');
        $this->setPrivateProperty($scheduler, 'tasks', $tasks);
        Services::injectMock('scheduler', $scheduler);

        return new TaskRunner();
    }
}
