<?php
declare(strict_types=1);
require dirname(__DIR__).'/vendor/autoload.php';
use Workshop\Stores;

$cfg = json_decode(file_get_contents($argv[1]), true, 512, JSON_THROW_ON_ERROR);
$events = [];
function event(string $kind, string $message, array $extra=[]): void {
    global $cfg, $events;
    $record = ['worker'=>$cfg['id'], 'kind'=>$kind, 'message'=>$message, 'at_ns'=>hrtime(true), ...$extra];
    $events[] = $record;
    file_put_contents($cfg['events'],json_encode($events,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT));
}
function waitFile(string $path): void {
    $deadline=microtime(true)+10;
    while(!is_file($path)) {if(microtime(true)>$deadline)throw new RuntimeException('Barrier timeout');usleep(5000);clearstatcache(true,$path);}
}
try {
    $factory=Stores::factory($cfg['store'],$cfg['directory']);
    $lock=$factory->createLock($cfg['key'], $cfg['ttl']??3.0, !($cfg['killOwner']??false));
    file_put_contents($cfg['ready'],'ready');
    waitFile($cfg['go']);
    if(isset($cfg['after']))waitFile($cfg['after']);
    usleep(($cfg['delayMs']??0)*1000);
    event('attempt','Запрос блокировки',['key'=>$cfg['displayKey'],'blocking'=>$cfg['blocking']??false]);
    if(!$lock->acquire($cfg['blocking']??false)) {event('denied','Занято: работа пропущена');exit(0);}
    event('acquired','Блокировка получена');
    if(isset($cfg['acquiredFlag']))file_put_contents($cfg['acquiredFlag'],'acquired');
    if($cfg['killOwner']??false) {event('hold','Владелец ждёт принудительного завершения');usleep(9000000);exit(2);}
    try {
        if($cfg['ownership']??false) {
            event('reentry','Повторный acquire того же объекта',['acquired'=>$lock->acquire()]);
            $other=$factory->createLock($cfg['key'],3);
            event('other_object','Другой объект не становится владельцем',['acquired'=>$other->acquire()]);
            $other->release();
            event('owner_check','Чужой release не снял блокировку',['isAcquired'=>$lock->isAcquired()]);
        }
        if($cfg['refresh']??false) {
            for($i=1;$i<=4;$i++) {usleep(180000);$lock->refresh();event('refresh','Аренда продлена',['remaining'=>round($lock->getRemainingLifetime(),3)]);}
        } else {
            usleep(($cfg['holdMs']??400)*1000);
        }
        if($cfg['throw']??false)throw new RuntimeException('Учебная ошибка обработки');
        if($cfg['expired']??false) {
            event('expired','Старый процесс проснулся после TTL',['isAcquired'=>$lock->isAcquired(),'isExpired'=>$lock->isExpired()]);
            try {$lock->refresh(); event('unexpected','Неожиданное продление');}
            catch(Throwable $e){event('refresh_failed','Продлить потерянную аренду нельзя',['exception'=>get_class($e)]);}
            event('skipped_write','Запись результата отменена: владение потеряно');
        } else {event('work','Учебная операция завершена');}
    } catch(Throwable $e) {
        event('error',$e->getMessage(),['exception'=>get_class($e)]);
    } finally {
        $lock->release();event('released','Вызван release в finally');
    }
} catch(Throwable $e) {event('fatal','Опыт не выполнен',['exception'=>get_class($e),'detail'=>$e->getMessage()]);exit(1);}
