<?php

declare(strict_types=1);

// Approved non-production DM Store runtime only. Output contains no credentials or real names.
$root = (string)getenv('POC_APP_ROOT');
$base = rtrim((string)getenv('POC_BASE_URL'), '/');
$runtimePid = (string)getenv('POC_RUNTIME_PID');
if ((string)getenv('POC_DIALECT') !== 'dm'
    || strpos($root, '/tmp/zhct-') !== 0
    || !is_file($root . '/application/base/Security.php')
    || !is_file($root . '/application/p/controller/Department.php')
    || !preg_match('~^http://127\.0\.0\.1:[0-9]+$~', $base)
    || !ctype_digit($runtimePid)) {
    throw new RuntimeException('Approved isolated Store DM runtime was not selected');
}

$boundaryRaw = @file_get_contents($base . '/__poc_boundary', false,
    stream_context_create(['http' => ['timeout' => 5]]));
$boundary = is_string($boundaryRaw) ? json_decode($boundaryRaw, true) : null;
if (!is_array($boundary) || empty($boundary['external_stubs']) || empty($boundary['redis_isolated'])) {
    throw new RuntimeException('Store HTTP external/Redis isolation boundary not proved');
}

$env = [];
foreach (['POC_HOST', 'POC_PORT', 'POC_SCHEMA', 'POC_USER', 'POC_PASSWORD'] as $key) {
    $env[$key] = (string)getenv($key);
}
if ($env['POC_HOST'] === '') {
    foreach (explode("\0", (string)@file_get_contents('/proc/' . $runtimePid . '/environ')) as $entry) {
        $parts = explode('=', $entry, 2);
        if (count($parts) === 2 && array_key_exists($parts[0], $env)) {
            $env[$parts[0]] = $parts[1];
        }
    }
}
if ($env['POC_HOST'] === '' || $env['POC_PORT'] === '' || $env['POC_SCHEMA'] === ''
    || $env['POC_USER'] === '' || $env['POC_PASSWORD'] === '') {
    throw new RuntimeException('DM runtime connection context unavailable');
}
$db = new PDO('dm:host=' . $env['POC_HOST'] . ';port=' . $env['POC_PORT']
    . ';schema=' . $env['POC_SCHEMA'], $env['POC_USER'], $env['POC_PASSWORD'],
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER,
        PDO::ATTR_TIMEOUT => 5]);
unset($env);

$table = static function (string $name): string { return 'YDY_' . strtoupper($name); };
$rows = static function (string $sql, array $args = []) use ($db): array {
    $query = $db->prepare($sql);
    $query->execute($args);
    return $query->fetchAll(PDO::FETCH_ASSOC);
};
$one = static function (string $sql, array $args = []) use ($rows): array {
    $found = $rows($sql, $args);
    return $found[0] ?? [];
};
$count = static function (string $sql, array $args = []) use ($one): int {
    return (int)($one($sql, $args)['n'] ?? -1);
};
$assert = static function (bool $ok, string $message): void {
    if (!$ok) throw new RuntimeException($message);
};
$post = static function (string $route, array $form, string $token = '') use ($base): array {
    $headers = "Content-Type: application/x-www-form-urlencoded\r\n";
    if ($token !== '') $headers .= 'token: ' . $token . "\r\n";
    $context = stream_context_create(['http' => [
        'method' => 'POST', 'header' => $headers,
        'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986),
        'timeout' => 20, 'ignore_errors' => true,
    ]]);
    $body = @file_get_contents($base . $route, false, $context);
    $responseHeaders = $http_response_header ?? [];
    preg_match('/HTTP\/\S+\s+(\d+)/', (string)($responseHeaders[0] ?? ''), $match);
    $json = is_string($body) ? json_decode($body, true) : null;
    return ['http' => isset($match[1]) ? (int)$match[1] : 0,
        'json' => is_array($json) ? $json : null];
};
$pass = static function (array $response, int $businessCode = 0): bool {
    return $response['http'] === 200
        && is_array($response['json'])
        && (int)($response['json']['code'] ?? -1) === $businessCode;
};
$expectedRootCode = static function () use ($rows, $table): string {
    $max = null;
    foreach ($rows('SELECT CODE FROM ' . $table('department') . ' WHERE PUUID=?', ['0']) as $row) {
        $code = (string)($row['code'] ?? '');
        if ($code === '' || !ctype_digit($code)) continue;
        $number = ltrim($code, '0');
        $number = $number === '' ? '0' : $number;
        if ($max === null || strlen($number) > strlen($max)
            || (strlen($number) === strlen($max) && strcmp($number, $max) > 0)) $max = $number;
    }
    if ($max === null) return '01';
    $digits = str_split($max);
    $carry = 1;
    for ($i = count($digits) - 1; $i >= 0 && $carry; --$i) {
        $digit = (int)$digits[$i] + $carry;
        $digits[$i] = (string)($digit % 10);
        $carry = $digit > 9 ? 1 : 0;
    }
    if ($carry) array_unshift($digits, '1');
    return str_pad(implode('', $digits), 2, '0', STR_PAD_LEFT);
};

$required = [
    'YDY_DEPARTMENT' => ['ID', 'UUID', 'PUUID', 'CODE', 'NAME', 'CHARGE_BY', 'LEADER',
        'LEAF_FLAG', 'STATUS', 'IS_SHOW', 'DEPARTMENT_PATH', 'CREATE_TIME', 'CREATE_BY'],
    'YDY_USER_DATA' => ['ID', 'USER_UUID', 'DATA_UUID', 'TYPE'],
    'YDY_USER' => ['ID', 'UUID', 'ACCOUNT', 'PWD', 'SALT', 'NAME', 'TYPE',
        'CREATE_TIME', 'CREATE_BY', 'IS_DELETE'],
    'YDY_STAFF_DEPARTMENT' => ['DEPARTMENT_UUID', 'STAFF_UUID'],
    'YDY_STAFF' => ['UUID', 'STATUS'],
];
foreach ($required as $name => $columns) {
    $actual = array_column($rows('SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME=?', [$name]), 'column_name');
    $assert(count(array_diff($columns, $actual)) === 0, 'Required Store DM table columns absent: ' . $name);
}

if ((string)getenv('POC_PREFLIGHT_ONLY') === '1') {
    echo json_encode(['suite' => 'store-department-c', 'mode' => 'read-only-preflight',
        'runtime_isolated' => true, 'required_columns_present' => true,
        'existing_test_department_rows' => $count('SELECT COUNT(*) N FROM ' . $table('department')
            . ' WHERE NAME LIKE ?', ['CDXPARDEPT%']),
        'existing_test_operator_rows' => $count('SELECT COUNT(*) N FROM ' . $table('user')
            . ' WHERE ACCOUNT LIKE ?', ['CDXPARDEPT%'])], JSON_UNESCAPED_SLASHES) . PHP_EOL;
    exit(0);
}

require_once $root . '/application/base/Security.php';
$marker = 'CDXPARDEPT' . strtoupper(bin2hex(random_bytes(4)));
$nameA = $marker . 'A';
$nameB = $marker . 'B';
$nameBad = $marker . 'X';
$operatorId = 0;
$departmentId = 0;
$departmentUuid = '';
$results = [];
$cleanup = [];
$failure = '';
$staffSummary = static function () use ($one, $table): string {
    $summary = $one('SELECT COUNT(*) N, COALESCE(SUM(STATUS),0) S FROM ' . $table('staff'));
    return hash('sha256', json_encode([$summary['n'] ?? null, $summary['s'] ?? null]));
};

try {
    $assert($count('SELECT COUNT(*) N FROM ' . $table('user') . ' WHERE UUID=? OR ACCOUNT=?',
        [$marker, $marker]) === 0, 'Synthetic operator marker already exists');
    $assert($count('SELECT COUNT(*) N FROM ' . $table('department') . ' WHERE NAME IN (?,?,?)',
        [$nameA, $nameB, $nameBad]) === 0, 'Synthetic department marker already exists');
    $assert($count('SELECT COUNT(*) N FROM ' . $table('user_data') . ' WHERE USER_UUID=?',
        [$marker]) === 0, 'Synthetic permission marker already exists');
    $results['preflight_marker_zero'] = true;

    // Every write route is behind the same AdminController::checkLogin gate.
    $unauth = $post('/p/department/add', ['puuid' => 'classA', 'code' => '1', 'name' => $nameA]);
    $assert($pass($unauth, 100), 'Department add did not reject unauthenticated request');
    $assert($count('SELECT COUNT(*) N FROM ' . $table('department') . ' WHERE NAME=?', [$nameA]) === 0,
        'Unauthenticated add unexpectedly wrote');
    $results['add_unauth_rejected_zero_write'] = true;

    $insert = $db->prepare('INSERT INTO ' . $table('user')
        . ' (UUID,ACCOUNT,PWD,SALT,NAME,TYPE,CREATE_TIME,CREATE_BY,IS_DELETE)'
        . ' VALUES (?,?,?,?,?,?,?,?,?)');
    $insert->execute([$marker, $marker, 'DISABLED', strtoupper(bin2hex(random_bytes(8))),
        $marker, 0, date('Y-m-d H:i:s'), 'CODEX', 0]);
    $operatorId = (int)($one('SELECT ID FROM ' . $table('user')
        . ' WHERE UUID=? AND ACCOUNT=?', [$marker, $marker])['id'] ?? 0);
    $assert($operatorId > 0, 'Synthetic operator row missing');
    $token = \app\base\Security::encrypt(implode("\t",
        ['0', $marker, $marker, '0', '', '', $marker]));

    $codeResponse = $post('/p/department/code', ['uuid' => 'classA'], $token);
    $candidate = $codeResponse['json']['data'] ?? null;
    $assert($pass($codeResponse) && (is_string($candidate) || is_int($candidate))
        && ctype_digit((string)$candidate), 'Department code route failed');
    if ((string)getenv('POC_EXPECT_CODE_FIXED') === '1') {
        $assert((string)$candidate === $expectedRootCode(),
            'Department code differs from numeric sibling maximum plus one');
        $results['code_equals_numeric_max_plus_one'] = true;
    }
    $assert($count('SELECT COUNT(*) N FROM ' . $table('department')
        . ' WHERE PUUID=? AND CODE=? AND IS_SHOW=1', ['0', (string)$candidate]) === 0,
        'Generated department code is occupied');
    $results['code_read_candidate_unoccupied'] = true;

    if ((string)getenv('POC_EXPECT_CODE_FIXED') === '1') {
        $hex = strtoupper(bin2hex(random_bytes(16)));
        $badParent = substr($hex, 0, 8) . '-' . substr($hex, 8, 4) . '-'
            . substr($hex, 12, 4) . '-' . substr($hex, 16, 4) . '-' . substr($hex, 20, 12);
        $assert($count('SELECT COUNT(*) N FROM ' . $table('department')
            . ' WHERE UUID=?', [$badParent]) === 0, 'Bad-parent UUID unexpectedly exists');
        $bad = $post('/p/department/add', ['puuid' => $badParent, 'code' => (string)$candidate,
            'name' => $nameBad, 'charge_by' => '', 'leader' => ''], $token);
        $assert($pass($bad, 1)
            && $count('SELECT COUNT(*) N FROM ' . $table('department') . ' WHERE NAME=?', [$nameBad]) === 0
            && $count('SELECT COUNT(*) N FROM ' . $table('user_data')
                . ' WHERE USER_UUID=?', [$marker]) === 0,
            'Nonexistent parent was not rejected with zero writes');
        $results['nonexistent_parent_rejected_zero_write'] = true;
    }

    $add = $post('/p/department/add', ['puuid' => 'classA', 'code' => (string)$candidate,
        'name' => $nameA, 'charge_by' => '', 'leader' => '', 'leaf_flag' => 0,
        'status' => 1, 'is_show' => 1], $token);
    $assert($pass($add), 'Department add HTTP/business response failed');
    $departmentRows = $rows('SELECT ID,UUID,PUUID,CODE,NAME,LEAF_FLAG,STATUS,IS_SHOW,CREATE_BY'
        . ' FROM ' . $table('department') . ' WHERE NAME=? AND CREATE_BY=?', [$nameA, $marker]);
    $assert(count($departmentRows) === 1, 'Department add source row not unique');
    $department = $departmentRows[0];
    $departmentId = (int)$department['id'];
    $departmentUuid = trim((string)$department['uuid']);
    $assert($departmentId > 0 && $departmentUuid !== ''
        && (string)$department['puuid'] === '0'
        && (string)$department['code'] === (string)$candidate
        && (int)$department['leaf_flag'] === 0 && (int)$department['status'] === 1
        && (int)$department['is_show'] === 1, 'Department add row values diverged');
    $assert($count('SELECT COUNT(*) N FROM ' . $table('user_data')
        . ' WHERE USER_UUID=? AND DATA_UUID=? AND TYPE=1', [$marker, $departmentUuid]) === 1,
        'Department own permission row absent');
    $results['add_department_and_user_data_readback'] = true;

    $update = $post('/p/department/update', ['uuid' => $departmentUuid,
        'puuid' => 'classA', 'code' => (string)$candidate, 'name' => $nameB,
        'charge_by' => '', 'leader' => ''], $token);
    $assert($pass($update), 'Department update HTTP/business response failed');
    $updated = $one('SELECT ID,UUID,NAME,PUUID,CODE,STATUS,IS_SHOW FROM ' . $table('department')
        . ' WHERE ID=? AND UUID=?', [$departmentId, $departmentUuid]);
    $assert((string)($updated['name'] ?? '') === $nameB
        && (string)($updated['puuid'] ?? '') === '0'
        && (string)($updated['code'] ?? '') === (string)$candidate
        && (int)($updated['status'] ?? -1) === 1
        && (int)($updated['is_show'] ?? -1) === 1, 'Department update source row diverged');
    $results['update_own_department_readback'] = true;

    $children = $count('SELECT COUNT(*) N FROM ' . $table('department')
        . ' WHERE PUUID=? AND UUID<>?', [$departmentUuid, $departmentUuid]);
    $linkedStaff = $count('SELECT COUNT(*) N FROM ' . $table('staff_department')
        . ' WHERE DEPARTMENT_UUID=?', [$departmentUuid]);
    $pathHits = $count('SELECT COUNT(*) N FROM ' . $table('department')
        . ' WHERE UUID<>? AND DEPARTMENT_PATH LIKE ?', [$departmentUuid, '%' . $departmentUuid . '%']);
    $beforeStaff = $staffSummary();
    $ableSafe = $children === 0 && $linkedStaff === 0 && $pathHits === 0
        && (int)$department['leaf_flag'] === 0 && (string)$department['puuid'] === '0';
    $results['able_preflight_empty_tree_zero_staff'] = $ableSafe;
    if ($ableSafe) {
        $disable = $post('/p/department/able', ['uuid' => $departmentUuid], $token);
        $afterDisable = $one('SELECT STATUS FROM ' . $table('department')
            . ' WHERE ID=? AND UUID=?', [$departmentId, $departmentUuid]);
        $assert($pass($disable) && (int)($afterDisable['status'] ?? -1) === 0
            && hash_equals($beforeStaff, $staffSummary()), 'Department disable or staff guard failed');
        $results['able_disable_own_row_no_staff_change'] = true;

        $enable = $post('/p/department/able', ['uuid' => $departmentUuid], $token);
        $afterEnable = $one('SELECT STATUS FROM ' . $table('department')
            . ' WHERE ID=? AND UUID=?', [$departmentId, $departmentUuid]);
        $assert($pass($enable) && (int)($afterEnable['status'] ?? -1) === 1
            && hash_equals($beforeStaff, $staffSummary()), 'Department enable or staff guard failed');
        $results['able_enable_own_row_no_staff_change'] = true;
    } else {
        $results['able_blocked_safety'] = true;
    }

    $delete = $post('/p/department/delete', ['uuid' => $departmentUuid], $token);
    $deleted = $one('SELECT IS_SHOW FROM ' . $table('department')
        . ' WHERE ID=? AND UUID=?', [$departmentId, $departmentUuid]);
    $assert($pass($delete) && (int)($deleted['is_show'] ?? -1) === 0,
        'Department soft-delete response or source row failed');
    $results['delete_soft_own_row_readback'] = true;
} catch (Throwable $e) {
    // Do not echo raw exception text: DB diagnostics may contain connection context.
    $failure = get_class($e) . ':' . hash('sha256', $e->getMessage());
} finally {
    // Exact cleanup only: this run's marker, generated UUID/ID, and two candidate names.
    try {
        $ownedRows = $rows('SELECT ID,UUID,NAME,CREATE_BY FROM ' . $table('department')
            . ' WHERE NAME IN (?,?,?) AND CREATE_BY=?', [$nameA, $nameB, $nameBad, $marker]);
        if (count($ownedRows) > 1) throw new RuntimeException('Ambiguous synthetic department cleanup');
        if (count($ownedRows) === 1) {
            $owned = $ownedRows[0];
            $ownedId = (int)$owned['id'];
            $ownedUuid = trim((string)$owned['uuid']);
            if ($departmentId > 0 && ($ownedId !== $departmentId || $ownedUuid !== $departmentUuid)) {
                throw new RuntimeException('Synthetic department identity mismatch during cleanup');
            }
            $permissionRows = $rows('SELECT ID FROM ' . $table('user_data')
                . ' WHERE USER_UUID=? AND DATA_UUID=? AND TYPE=1', [$marker, $ownedUuid]);
            foreach ($permissionRows as $permission) {
                $statement = $db->prepare('DELETE FROM ' . $table('user_data')
                    . ' WHERE ID=? AND USER_UUID=? AND DATA_UUID=? AND TYPE=1');
                $statement->execute([(int)$permission['id'], $marker, $ownedUuid]);
            }
            $statement = $db->prepare('DELETE FROM ' . $table('department')
                . ' WHERE ID=? AND UUID=? AND NAME=? AND CREATE_BY=?');
            $statement->execute([$ownedId, $ownedUuid, (string)$owned['name'], $marker]);
        }
        if ($operatorId > 0) {
            $statement = $db->prepare('DELETE FROM ' . $table('user')
                . ' WHERE ID=? AND UUID=? AND ACCOUNT=?');
            $statement->execute([$operatorId, $marker, $marker]);
        }
        $cleanup = [
            'operator_zero' => $count('SELECT COUNT(*) N FROM ' . $table('user')
                . ' WHERE UUID=? OR ACCOUNT=?', [$marker, $marker]) === 0,
            'department_zero' => $count('SELECT COUNT(*) N FROM ' . $table('department')
                . ' WHERE NAME IN (?,?,?)', [$nameA, $nameB, $nameBad]) === 0,
            'permission_zero' => $count('SELECT COUNT(*) N FROM ' . $table('user_data')
                . ' WHERE USER_UUID=?', [$marker]) === 0,
        ];
    } catch (Throwable $e) {
        $cleanup['error_class'] = get_class($e);
        $cleanup['error_sha256'] = hash('sha256', $e->getMessage());
    }
}

$clean = ($cleanup['operator_zero'] ?? false)
    && ($cleanup['department_zero'] ?? false)
    && ($cleanup['permission_zero'] ?? false);
echo json_encode(['suite' => 'store-department-c', 'dialect' => 'dm',
    'runtime_isolated' => true, 'marker' => $marker, 'results' => $results,
    'failure' => $failure, 'cleanup' => $cleanup],
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
if ($failure !== '' || !$clean) exit(1);
