<?php
declare(strict_types=1);

// Read-only natural-cache gate. Does not create a token or invoke business HTTP.
$lane = (string)($argv[1] ?? '');
if (!in_array($lane, ['B', 'C'], true)) {
    fwrite(STDERR, "DENIED: B or C required\n");
    exit(78);
}
$root = $lane === 'B' ? '/private/tmp/zhct-http-ai-macos.x2CCZX'
    : '/tmp/zhct-ai-http-20260929.3XDj3w';
$pid = $lane === 'B' ? 13127 : 53772;
$port = $lane === 'B' ? 18101 : 18102;
$sources = ['app/api/controller/meal/Meal.php',
    'app/api/service/meal/Meal.php',
    'app/common/model/meal/MealSuggestion.php',
    'app/api/model/User.php'];
foreach ($sources as $file) {
    if (!is_file($root . '/' . $file)) throw new RuntimeException('Worker source absent');
}
if ($lane === 'B') {
    $cfg = parse_ini_file('/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local', true, INI_SCANNER_RAW)['DATABASE'] ?? null;
    $ps = (string)shell_exec('/bin/ps eww -p ' . $pid . ' -o command=');
    $owner = trim((string)shell_exec('/usr/sbin/lsof -nP -t -iTCP:' . $port . ' -sTCP:LISTEN 2>/dev/null'));
    if (!is_array($cfg) || strtolower((string)($cfg['TYPE'] ?? '')) !== 'mysql'
        || (string)($cfg['HOSTPORT'] ?? '') !== '3306' || $owner !== (string)$pid
        || strpos($ps, 'POC_DIALECT=mysql') === false
        || strpos($ps, 'POC_EXTERNAL_STUBS=1') === false) {
        throw new RuntimeException('B worker/config drift');
    }
    foreach (['HOSTNAME' => 'HOST', 'HOSTPORT' => 'PORT', 'DATABASE' => 'SCHEMA',
              'USERNAME' => 'USER', 'PASSWORD' => 'PASSWORD'] as $from => $to) {
        $v = (string)($cfg[$from] ?? '');
        if ($v === '' || strpos($ps, 'POC_AI_' . $to . '=' . $v) === false) {
            throw new RuntimeException('B worker differs from code local config');
        }
    }
    $db = new PDO('mysql:host=' . $cfg['HOSTNAME'] . ';port=' . $cfg['HOSTPORT']
        . ';dbname=' . $cfg['DATABASE'] . ';charset=utf8', $cfg['USERNAME'], $cfg['PASSWORD'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
} else {
    $env = [];
    foreach (explode("\0", (string)file_get_contents('/proc/' . $pid . '/environ')) as $item) {
        $pair = explode('=', $item, 2);
        if (count($pair) === 2) $env[$pair[0]] = $pair[1];
    }
    if (($env['POC_TARGET_ROOT'] ?? '') !== $root
        || ($env['POC_BASE_URL'] ?? '') !== 'http://127.0.0.1:' . $port
        || ($env['POC_DIALECT'] ?? '') !== 'dm'
        || ($env['POC_EXTERNAL_STUBS'] ?? '') !== '1'
        || ($env['POC_AI_HOST'] ?? '') !== '127.0.0.1'
        || ($env['POC_AI_PORT'] ?? '') !== '5238'
        || ($env['POC_AI_SCHEMA'] ?? '') !== 'AIZHCT_APP'
        || ($env['POC_AI_USER'] ?? '') !== 'AIZHCT_APP'
        || !in_array('dm', PDO::getAvailableDrivers(), true)) {
        throw new RuntimeException('C worker/config drift');
    }
    $db = new PDO('dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP',
        $env['POC_AI_USER'], $env['POC_AI_PASSWORD'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
    if ($db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn() !== 'AIZHCT_APP') {
        throw new RuntimeException('C schema drift');
    }
}
$t = static function (string $logical) use ($lane): string {
    if (!in_array($logical, ['meal_suggestion', 'meal', 'staff', 'user', 'store'], true)) {
        throw new RuntimeException('Unexpected table');
    }
    return 'yoshop_' . ($lane === 'B' ? $logical : strtoupper($logical));
};
$count = static function (PDO $d, string $sql, array $args = []): int {
    $s = $d->prepare($sql);
    $s->execute($args);
    return (int)$s->fetchColumn();
};
$counts = [];
foreach (['meal_suggestion', 'meal', 'staff', 'user', 'store'] as $logical) {
    $counts[$logical] = $count($db, 'SELECT COUNT(*) FROM ' . $t($logical)
        . ' WHERE STORE_ID=10001');
}
$s = $db->query('SELECT ID,STAFF_ID,MEAL_DATE,MEAL_TIMES,UPDATE_TIME FROM '
    . $t('meal_suggestion')
    . ' WHERE STORE_ID=10001 AND TYPE=1 ORDER BY ID DESC');
$type1Count = $count($db, 'SELECT COUNT(*) FROM ' . $t('meal_suggestion')
    . ' WHERE STORE_ID=10001 AND TYPE=1');
$scanned = 0;
$safe = [];
$reasons = ['staff_or_user_missing' => 0, 'not_latest' => 0,
    'meal_newer' => 0, 'empty_output' => 0];
while (($r = $s->fetch(PDO::FETCH_ASSOC)) !== false && $scanned < 1000) {
    $scanned++;
    $id = (int)$r['id'];
    $staffId = (int)$r['staff_id'];
    $date = (string)$r['meal_date'];
    $times = (int)$r['meal_times'];
    $staff = $db->prepare('SELECT USER_ID FROM ' . $t('staff')
        . ' WHERE ID=? AND STORE_ID=10001 AND IS_DELETE=0');
    $staff->execute([$staffId]);
    $userId = (int)$staff->fetchColumn();
    if ($userId <= 0 || $count($db, 'SELECT COUNT(*) FROM ' . $t('user')
        . ' WHERE USER_ID=? AND STORE_ID=10001 AND IS_DELETE=0', [$userId]) !== 1) {
        $reasons['staff_or_user_missing']++;
        continue;
    }
    $latest = $count($db, 'SELECT MAX(ID) FROM ' . $t('meal_suggestion')
        . ' WHERE STAFF_ID=? AND TYPE=1 AND MEAL_DATE=? AND MEAL_TIMES=? AND IS_DELETE=0',
        [$staffId, $date, $times]);
    if ($latest !== $id) {
        $reasons['not_latest']++;
        continue;
    }
    $newer = $count($db, 'SELECT COUNT(*) FROM ' . $t('meal') . ' m'
        . ' JOIN ' . $t('meal_suggestion') . ' s ON s.ID=?'
        . ' WHERE m.STAFF_ID=s.STAFF_ID AND m.MEAL_DATE=s.MEAL_DATE'
        . ' AND m.MEAL_TIMES=s.MEAL_TIMES AND m.IS_DELETE=0'
        . ' AND m.UPDATE_TIME>s.UPDATE_TIME', [$id]);
    if ($newer > 0) {
        $reasons['meal_newer']++;
        continue;
    }
    $suggestion = $db->prepare('SELECT SUGGEST_OUTPUT,TIPS_OUTPUT FROM '
        . $t('meal_suggestion') . ' WHERE ID=?');
    $suggestion->execute([$id]);
    $text = $suggestion->fetch(PDO::FETCH_ASSOC);
    if (is_array($text)) {
        foreach (['suggest_output', 'tips_output'] as $field) {
            if (is_resource($text[$field])) {
                $text[$field] = stream_get_contents($text[$field]);
            }
        }
    }
    if (!is_array($text)
        || trim((string)$text['suggest_output']) === ''
        || trim((string)$text['tips_output']) === '') {
        $reasons['empty_output']++;
        continue;
    }
    $safe[] = ['suggestion_id' => $id, 'staff_id' => $staffId,
        'user_id' => $userId, 'meal_date' => $date, 'meal_times' => $times,
        'suggestion_update_time' => (string)$r['update_time'],
        'suggestion_text_sha256' => hash('sha256',
            (string)$text['suggest_output'] . "\0" . (string)$text['tips_output'])];
    if (count($safe) >= 8) break;
}
$sourceSha = [];
foreach ($sources as $file) $sourceSha[$file] = hash_file('sha256', $root . '/' . $file);
echo json_encode(['lane' => $lane, 'worker_port' => $port,
    'source_sha256' => $sourceSha, 'source_counts' => $counts,
    'type1_suggestion_rows' => $type1Count, 'scanned_rows' => $scanned,
    'safe_cached_candidates' => $safe,
    'excluded_reasons' => $reasons,
    'business_http' => 0, 'business_dml' => 0], JSON_UNESCAPED_SLASHES), "\n";
