#!/usr/bin/env php
<?php
/**
 * ptyRun - 스크립트 중복 실행 제어 및 타임아웃 관리
 *
 * Usage: ./ptyRun "스크립트명" [옵션]
 *
 * 옵션:
 *   --run-duplicate=true|false  중복 실행 허용 여부 (기본값: false)
 *   --max-run-time=분           최대 실행 시간(분), 초과 시 강제 종료 후 재실행 (기본값: 0 - 무제한)
 *   --help                      도움말 출력
 *
 * 예제:
 *   ./ptyRun "php /path/to/script.php" --max-run-time=20
 */

namespace platyFramework;

require_once __DIR__ . '/ptyLibrary_PHP/cli/ptyCliOptionParser.php';
require_once __DIR__ . '/ptyLibrary_PHP/cli/ptyCliLog.php';

// 인자 파싱
$parsed = ptyCliOptionParser::parse($argv);
$positionalArgs = $parsed['positional'];
$options = $parsed['options'];

// 도움말
if (empty($positionalArgs) || isset($options['help'])) {
    echo "사용법: {$argv[0]} \"스크립트명\" [옵션]\n";
    echo "\n";
    echo "옵션:\n";
    echo "  --run-duplicate=true|false  중복 실행 허용 여부 (기본값: false)\n";
    echo "  --max-run-time=분           최대 실행 시간(분), 초과 시 강제 종료 후 재실행 (기본값: 0 - 무제한)\n";
    echo "  --help                      도움말 출력\n";
    echo "\n";
    echo "예제:\n";
    echo "  {$argv[0]} \"php /path/to/script.php\" --max-run-time=20\n";
    echo "  {$argv[0]} \"php script.php\" --run-duplicate=true\n";
    echo "  {$argv[0]} \"./myScript.sh\"\n";
    echo "\n";
    exit(isset($options['help']) ? 0 : 1);
}

$scriptCommand = $positionalArgs[0];
$runDuplicate = isset($options['run-duplicate']) && $options['run-duplicate'] === 'true';
$maxRunTime = isset($options['max-run-time']) ? (int)$options['max-run-time'] : 0;

// 로거 인스턴스 생성
$log = ptyCliLog("RUN", ptyCliLog::COLOR_CYAN, true);

// 락 파일명 생성 (스크립트 경로를 해시로 변환)
$lockFile = '/tmp/ptyRun_' . md5($scriptCommand) . '.lock';
$pidFile = '/tmp/ptyRun_' . md5($scriptCommand) . '.pid';

try {
    // 중복 실행 체크
    if (!$runDuplicate) {
        if (file_exists($lockFile)) {
            $lockData = json_decode(file_get_contents($lockFile), true);
            $pid = $lockData['pid'] ?? 0;
            $startTime = $lockData['start_time'] ?? 0;

            // 프로세스가 실제로 실행 중인지 확인
            if ($pid && posix_kill($pid, 0)) {
                // 최대 실행 시간 체크
                if ($maxRunTime > 0 && $startTime > 0) {
                    $elapsedMinutes = (time() - $startTime) / 60;
                    if ($elapsedMinutes > $maxRunTime) {
                        $log->warning("최대 실행 시간({$maxRunTime}분) 초과. 프로세스(PID: {$pid})를 강제 종료합니다.");
                        posix_kill($pid, SIGTERM);
                        sleep(2);

                        // SIGTERM으로 종료되지 않으면 SIGKILL
                        if (posix_kill($pid, 0)) {
                            posix_kill($pid, SIGKILL);
                            sleep(1);
                        }

                        @unlink($lockFile);
                        @unlink($pidFile);
                    } else {
                        $log->info("스크립트가 이미 실행 중입니다. (PID: {$pid}, 경과: " . round($elapsedMinutes, 1) . "분)");
                        exit(0);
                    }
                } else {
                    $log->info("스크립트가 이미 실행 중입니다. (PID: {$pid})");
                    exit(0);
                }
            } else {
                // 프로세스가 없으면 락 파일 제거
                @unlink($lockFile);
                @unlink($pidFile);
            }
        }
    }

    // 스크립트 실행
    $log->info("스크립트 실행: {$scriptCommand}");

    $pid = pcntl_fork();

    if ($pid == -1) {
        throw new \Exception("프로세스 fork 실패");
    } elseif ($pid == 0) {
        // 자식 프로세스
        exec($scriptCommand, $output, $returnCode);
        exit($returnCode);
    } else {
        // 부모 프로세스
        if (!$runDuplicate) {
            // 락 파일 생성
            $lockData = [
                'pid' => $pid,
                'command' => $scriptCommand,
                'start_time' => time(),
            ];
            file_put_contents($lockFile, json_encode($lockData));
            file_put_contents($pidFile, $pid);
        }

        // 자식 프로세스 대기
        $status = 0;
        pcntl_waitpid($pid, $status);
        $exitCode = pcntl_wexitstatus($status);

        // 락 파일 제거
        if (!$runDuplicate) {
            @unlink($lockFile);
            @unlink($pidFile);
        }

        if ($exitCode === 0) {
            $log->success("스크립트 실행 완료");
        } else {
            $log->error("스크립트 실행 실패 (Exit Code: {$exitCode})");
        }

        exit($exitCode);
    }

} catch (\Exception $e) {
    $log->error("Error: " . $e->getMessage());
    @unlink($lockFile);
    @unlink($pidFile);
    exit(1);
}
