<?php

namespace App\Support;

use CodeIgniter\CLI\CLI;

/**
 * Non-blocking file lock so overlapping cron runs skip instead of stacking.
 * Unique indexes still remain the last line of defence.
 */
final class CommandLock
{
    /**
     * @param callable(): int $work
     */
    public static function run(string $name, callable $work): int
    {
        $dir = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR . 'cache';
        if (! is_dir($dir) && ! mkdir($dir, 0750, true) && ! is_dir($dir)) {
            CLI::error('Could not create the command lock directory.');

            return EXIT_ERROR;
        }

        $safe = preg_replace('/[^a-z0-9._-]+/i', '-', $name) ?? 'command';
        $path = $dir . DIRECTORY_SEPARATOR . $safe . '.lock';
        $handle = fopen($path, 'c');
        if ($handle === false) {
            CLI::error('Could not open the command lock file.');

            return EXIT_ERROR;
        }

        if (! flock($handle, LOCK_EX | LOCK_NB)) {
            CLI::write('Another instance is already running; skipping.', 'yellow');
            fclose($handle);

            return EXIT_SUCCESS;
        }

        try {
            return (int) $work();
        } finally {
            flock($handle, LOCK_UN);
            fclose($handle);
        }
    }
}
