<?php
declare(strict_types=1);

// Only the three fixed Store staff read routes; emits shapes/counts, never values.
$lane = (string)($argv[1] ?? '');
$mode = (string)($argv[2] ?? '');
if (!in_array($lane, ['B', 'C'], true) || !in_array($mode, ['preflight', 'run'], true)) {
    fwrite(STDERR, "DENIED: fixed lane/mode required\n");
    exit(78);
}
if ($lane === 'B') {
    $config = include '/tmp/zhct-http-store-mysql.dQTTBP/application/config/local/database.php';
    if (!is_array($config) || ($config['database'] ?? '') !== 'zhct') {
        throw new RuntimeException('B code-local Store database drift');
    }
    $db = new PDO('mysql:host=' . $config['hostname'] . ';port=3306;dbname='
        . $config['database'] . ';charset=utf8', $config['username'], $config['password'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
    $port = 18081;
} else {
    if (getenv('POC_DIALECT') !== 'dm' || getenv('POC_HOST') !== '127.0.0.1'
        || getenv('POC_PORT') !== '5237' || getenv('POC_SCHEMA') !== 'ZHCT_APP'
        || getenv('POC_USER') !== 'ZHCT_APP') {
        throw new RuntimeException('C isolated Store identity drift');
    }
    $db = new PDO('dm:host=127.0.0.1;port=5237;schema=ZHCT_APP',
        getenv('POC_USER'), getenv('POC_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() !== 'ZHCT_APP') {
        throw new RuntimeException('C schema drift');
    }
    $port = 18080;
}
$fixedUserUuid = '4A544AFA-7911-9C6A-124D-EFBF1684AAD0';
$count = static function (string $sql) use ($db): int {
    return (int)$db->query($sql)->fetchColumn();
};
$source = [
    'staff_visible' => $count('SELECT COUNT(*) FROM ydy_staff WHERE is_show=1'),
    'staff_visible_status1' => $count('SELECT COUNT(*) FROM ydy_staff WHERE is_show=1 AND status=1'),
    'department_active' => $count('SELECT COUNT(*) FROM ydy_department WHERE is_show=1 AND status=1'),
    'hardware_image_rows' => $count('SELECT COUNT(*) FROM ydy_hardware_img'),
];
$stmt = $db->prepare('SELECT type FROM ydy_user WHERE uuid=?');
$stmt->execute([$fixedUserUuid]);
$fixedUser = $stmt->fetch(PDO::FETCH_ASSOC);
$source['tree_fixed_user_exists'] = is_array($fixedUser);
$source['tree_fixed_user_superadmin'] = is_array($fixedUser)
    && empty(array_change_key_case($fixedUser, CASE_LOWER)['type']);
if ($mode === 'preflight') {
    echo json_encode(['lane' => $lane, 'source' => $source, 'business_http' => 0,
        'business_dml' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
$http = static function (string $action, string $form = '') use ($port): array {
    if (!in_array($action, ['lists', 'info', 'tree'], true)) {
        throw new RuntimeException('Unsupported staff route');
    }
    $context = stream_context_create(['http' => [
        'method' => 'POST', 'ignore_errors' => true, 'follow_location' => 0,
        'timeout' => 25, 'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
        'content' => $form,
    ]]);
    $handle = @fopen('http://127.0.0.1:' . $port . '/p/api/' . $action,
        'rb', false, $context);
    if (!is_resource($handle)) {
        throw new RuntimeException('Isolated Store HTTP absent: ' . $action);
    }
    $raw = stream_get_contents($handle, 8388609);
    fclose($handle);
    if (!is_string($raw) || strlen($raw) > 8388608) {
        throw new RuntimeException('Staff HTTP response over cap: ' . $action);
    }
    $body = json_decode($raw, true);
    if (!is_array($body)) {
        throw new RuntimeException('Staff HTTP non-JSON: ' . $action);
    }
    if ((int)($body['code'] ?? -1) !== 0) {
        throw new RuntimeException('Staff HTTP business failure: ' . $action
            . ' code=' . (string)($body['code'] ?? 'missing'));
    }
    return $body;
};
$types = static function (array $row): array {
    $result = [];
    foreach ($row as $key => $value) {
        $result[(string)$key] = gettype($value);
    }
    ksort($result);
    return $result;
};
$sameScalar = static function ($actual, $expected): bool {
    if ($actual === $expected) {
        return true;
    }
    if (!is_scalar($actual) || !is_scalar($expected)) {
        return false;
    }
    if ((string)$actual === (string)$expected) {
        return true;
    }
    $left = (string)$actual;
    $right = (string)$expected;
    if (!preg_match('/^-?[0-9]+(?:\.[0-9]+)?$/', $left)
        || !preg_match('/^-?[0-9]+(?:\.[0-9]+)?$/', $right)) {
        return false;
    }
    $canonical = static function (string $value): string {
        $negative = isset($value[0]) && $value[0] === '-';
        if ($negative) {
            $value = substr($value, 1);
        }
        $parts = explode('.', $value, 2);
        $whole = ltrim($parts[0], '0');
        $whole = $whole === '' ? '0' : $whole;
        $fraction = rtrim($parts[1] ?? '', '0');
        return ($negative && ($whole !== '0' || $fraction !== '') ? '-' : '')
            . $whole . ($fraction === '' ? '' : '.' . $fraction);
    };
    return $canonical($left) === $canonical($right);
};
$staffByUuid = $db->prepare('SELECT * FROM ydy_staff WHERE uuid=?');
$listBody = $http('lists', 'start=0&draw=3');
$rows = $listBody['data'] ?? null;
if (!is_array($rows) || !$rows || !array_key_exists(0, $rows)) {
    throw new RuntimeException('Staff lists lacks natural positive rows');
}
if (!in_array((int)($listBody['recordsTotal'] ?? -1),
        [$source['staff_visible'], $source['staff_visible_status1']], true)) {
    throw new RuntimeException('Staff lists total differs from original table');
}
if ((int)($listBody['recordsFiltered'] ?? -1) !== (int)$listBody['recordsTotal']
    || (int)($listBody['draw'] ?? -1) !== 3) {
    throw new RuntimeException('Staff lists paging envelope differs');
}
$listFields = ['id', 'uuid', 'personnel_umber', 'sex', 'mobile',
    'hardware_flag', 'hardware_time', 'sport_event_uuid', 'zp_card', 'zp_mob'];
$seen = [];
foreach ($rows as $row) {
    if (!is_array($row) || empty($row['uuid'])) {
        throw new RuntimeException('Staff lists row shape differs');
    }
    $uuid = (string)$row['uuid'];
    if (isset($seen[$uuid])) {
        throw new RuntimeException('Staff lists duplicate staff UUID');
    }
    $seen[$uuid] = true;
    $staffByUuid->execute([$uuid]);
    $original = $staffByUuid->fetch(PDO::FETCH_ASSOC);
    if (!is_array($original)) {
        throw new RuntimeException('Staff lists row missing original table');
    }
    $original = array_change_key_case($original, CASE_LOWER);
    if ((int)$original['is_show'] !== 1) {
        throw new RuntimeException('Staff lists row not visible in original table');
    }
    foreach ($listFields as $field) {
        if (!array_key_exists($field, $row)
            || !$sameScalar($row[$field], $original[$field] ?? null)) {
            throw new RuntimeException('Staff lists source mismatch: ' . $field);
        }
    }
    $expectedName = empty($original['nickname']) ? $original['name'] : $original['nickname'];
    if (!$sameScalar($row['name'] ?? null, $expectedName)) {
        throw new RuntimeException('Staff lists source mismatch: display name');
    }
    if (!isset($row['birthday']) || !is_numeric($row['birthday'])) {
        throw new RuntimeException('Staff lists age type differs');
    }
    $departmentQuery = $db->prepare('SELECT d.name FROM ydy_staff_department sd'
        . ' JOIN ydy_department d ON d.uuid=sd.department_uuid WHERE sd.staff_uuid=?');
    $departmentQuery->execute([$uuid]);
    $sourceDepartments = array_map('strval', $departmentQuery->fetchAll(PDO::FETCH_COLUMN));
    $actualDepartments = ($row['department_name'] ?? '') === '' ? []
        : explode(',', (string)$row['department_name']);
    sort($sourceDepartments, SORT_STRING);
    sort($actualDepartments, SORT_STRING);
    if ($sourceDepartments !== $actualDepartments) {
        throw new RuntimeException('Staff lists department join mismatch');
    }
}
$chosenUuid = (string)$rows[0]['uuid'];
$staffByUuid->execute([$chosenUuid]);
$original = array_change_key_case($staffByUuid->fetch(PDO::FETCH_ASSOC), CASE_LOWER);
$infoBody = $http('info', http_build_query(['uuid' => $chosenUuid]));
$infoRows = $infoBody['data'] ?? null;
if (!is_array($infoRows) || count($infoRows) !== 1 || !is_array($infoRows[0])) {
    throw new RuntimeException('Staff info lacks unique original row');
}
$info = $infoRows[0];
foreach ($original as $field => $expected) {
    if ($field === 'name') {
        $expected = empty($original['nickname']) ? $original['name'] : $original['nickname'];
    }
    if ($field === 'department_name') {
        $expected = $rows[0]['department_name'];
    }
    if (!array_key_exists($field, $info) || !$sameScalar($info[$field], $expected)) {
        throw new RuntimeException('Staff info source mismatch: ' . $field
            . ' original_type=' . gettype($expected)
            . ' http_type=' . gettype($info[$field] ?? null)
            . ' numeric_equal=' . ((is_numeric($expected) && is_numeric($info[$field] ?? null)
                && (float)$expected === (float)$info[$field]) ? 'yes' : 'no'));
    }
}
$treeBody = $http('tree');
$treeData = $treeBody['data'] ?? null;
if (!is_array($treeData) || !isset($treeData['department'], $treeData['project'])
    || !is_array($treeData['department']) || !is_array($treeData['project'])) {
    throw new RuntimeException('Staff tree contract differs');
}
$treeCount = 0;
$stack = $treeData['department'];
$departmentByUuid = $db->prepare('SELECT * FROM ydy_department WHERE uuid=?');
while ($stack) {
    $node = array_pop($stack);
    if (!is_array($node) || empty($node['uuid'])) {
        throw new RuntimeException('Staff tree node shape differs');
    }
    $departmentByUuid->execute([(string)$node['uuid']]);
    $dep = $departmentByUuid->fetch(PDO::FETCH_ASSOC);
    if (!is_array($dep)) {
        throw new RuntimeException('Staff tree node absent from original table');
    }
    $dep = array_change_key_case($dep, CASE_LOWER);
    foreach ($dep as $key => $value) {
        if (!array_key_exists($key, $node) || !$sameScalar($node[$key], $value)) {
            throw new RuntimeException('Staff tree original mismatch: ' . $key);
        }
    }
    if (!isset($node['chhildShow']) || $node['chhildShow'] !== false) {
        throw new RuntimeException('Staff tree UI flag differs');
    }
    foreach (($node['children'] ?? []) as $child) {
        $stack[] = $child;
    }
    $treeCount++;
    if ($treeCount > $source['department_active']) {
        throw new RuntimeException('Staff tree has duplicate/extra nodes');
    }
}
$result = [
    'lane' => $lane, 'source' => $source,
    'lists' => ['rows' => count($rows), 'source_rows_equal' => count($seen),
        'total' => (int)$listBody['recordsTotal'],
        'envelope_types' => $types($listBody), 'first_row_types' => $types($rows[0])],
    'info' => ['rows' => count($infoRows), 'source_columns_equal' => count($original),
        'row_types' => $types($info)],
    'tree' => ['department_nodes_source_equal' => $treeCount,
        'project_nodes' => count($treeData['project']),
        'envelope_types' => $types($treeData)],
    'business_http' => 3, 'business_dml' => 0,
];
echo json_encode($result, JSON_UNESCAPED_SLASHES), PHP_EOL;
