<?php

namespace MintyPHP\Tests\Service\Scheduler;

use MintyPHP\App\AppContainer;
use MintyPHP\App\Module\ModuleRegistry;
use MintyPHP\Service\Scheduler\Handler\ScheduledJobHandlerInterface;
use MintyPHP\Service\Scheduler\ScheduledJobRegistry;
use MintyPHP\Service\User\UserLifecycleService;
use PHPUnit\Framework\TestCase;

final class ScheduledJobRegistryTest extends TestCase
{
    private string $fixturesDir = '';

    protected function setUp(): void
    {
        $this->fixturesDir = sys_get_temp_dir() . '/scheduler-module-registry-' . uniqid();
        mkdir($this->fixturesDir, 0777, true);
    }

    protected function tearDown(): void
    {
        $this->removeDir($this->fixturesDir);
    }

    public function testModuleJobAppearsInDefinitionsAndIsExecutable(): void
    {
        $moduleRegistry = $this->createRegistryWithModuleJob();
        $container = new AppContainer();
        $container->set(ModuleRegistry::class, static fn (): ModuleRegistry => $moduleRegistry);
        $container->set(
            DummyModuleScheduledJobHandler::class,
            static fn (): DummyModuleScheduledJobHandler => new DummyModuleScheduledJobHandler()
        );

        $registry = new ScheduledJobRegistry(
            $this->createMock(UserLifecycleService::class),
            $container
        );

        $definitions = $registry->definitions();
        self::assertArrayHasKey('addressbook.sync', $definitions);
        self::assertSame('Address book sync', $definitions['addressbook.sync']['label'] ?? null);

        $execution = $registry->execute('addressbook.sync', 123);
        self::assertSame('success', $execution['status']);
        self::assertSame(['actor' => 123], $execution['result']);
    }

    private function createRegistryWithModuleJob(): ModuleRegistry
    {
        mkdir($this->fixturesDir . '/mod-job', 0777, true);
        file_put_contents(
            $this->fixturesDir . '/mod-job/module.php',
            '<?php return ' . var_export([
                'id' => 'mod-job',
                'scheduler_jobs' => [[
                    'job_key' => 'addressbook.sync',
                    'handler' => DummyModuleScheduledJobHandler::class,
                    'label' => 'Address book sync',
                    'description' => 'Sync job',
                    'default_enabled' => true,
                    'default_timezone' => 'UTC',
                    'default_schedule_type' => 'daily',
                    'default_schedule_interval' => 1,
                    'default_schedule_time' => '03:00',
                    'default_schedule_weekdays_csv' => null,
                    'default_catchup_once' => true,
                    'allowed_schedule_types' => ['daily', 'weekly'],
                ]],
            ], true) . ';'
        );

        return ModuleRegistry::boot($this->fixturesDir, ['mod-job']);
    }

    private function removeDir(string $dir): void
    {
        if (!is_dir($dir)) {
            return;
        }

        $items = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::CHILD_FIRST
        );

        foreach ($items as $item) {
            if ($item->isDir()) {
                rmdir($item->getPathname());
            } else {
                unlink($item->getPathname());
            }
        }

        rmdir($dir);
    }
}

final class DummyModuleScheduledJobHandler implements ScheduledJobHandlerInterface
{
    public function definition(): array
    {
        return [
            'label' => 'dummy',
            'description' => 'dummy',
            'default_enabled' => 1,
            'default_timezone' => 'UTC',
            'default_schedule_type' => 'daily',
            'default_schedule_interval' => 1,
            'default_schedule_time' => '00:00',
            'default_schedule_weekdays_csv' => null,
            'default_catchup_once' => 1,
            'allowed_schedule_types' => ['daily'],
        ];
    }

    public function execute(?int $actorUserId): array
    {
        return [
            'status' => 'success',
            'error_code' => null,
            'error_message' => null,
            'result' => ['actor' => $actorUserId],
        ];
    }
}
