<?php

declare(strict_types=1);

$mode = (string)($argv[1] ?? '');
$lane = (string)($argv[2] ?? '');
$marker = (string)($argv[3] ?? '');
$selfSha = $lane === 'B' ? (string)hash_file('sha256', __FILE__) : '';

if (count($argv) !== 4
    || !in_array($mode, ['--preflight', '--execute', '--audit'], true)
    || !in_array($lane, ['B', 'C'], true)
    || !preg_match('/^CDXARTICLE[0-9A-F]{10}$/D', $marker)
    || ($mode === '--execute'
        && ((string)getenv('CDX_AW6_APPROVE') !== 'ROOT_AI_ARTICLE_WRITE6_V1'
            || ($lane === 'B'
                ? !hash_equals($selfSha, (string)getenv('CDX_AW6_SHA'))
                : (string)getenv('CDX_AW6_SOURCE_CHECKED') !== '1')))) {
    echo json_encode(['status' => 'default_deny', 'connected' => false,
        'http_sent' => false, 'dml' => false]), PHP_EOL;
    exit(78);
}

$stage = 'bootstrap';
set_exception_handler(static function (Throwable $e) use (&$stage): void {
    echo json_encode(['status' => 'redacted_stop', 'stage' => $stage,
        'class' => get_class($e), 'code' => (string)$e->getCode(),
        'message_sha' => hash('sha256', $e->getMessage())]), PHP_EOL;
    exit(2);
});

[$root, $pid, $port, $dialect] = $lane === 'B'
    ? ['/private/tmp/zhct-http-ai-macos.x2CCZX', 13127, 18101, 'mysql']
    : ['/tmp/zhct-ai-http-20260929.3XDj3w', 53772, 18102, 'dm'];
$base = 'http://127.0.0.1:' . $port;

$sourceHashes = [
    '/app/store/controller/content/Article.php' => '05c0d0f85d3ef7d214afc4c64204c112099ae54baa843f36031d7f6744a08f74',
    '/app/store/controller/content/article/Category.php' => '981d145a9407b9beae3b255bd66b41a7ed66b96e1046e6a64b1af0965b67af9f',
    '/app/store/model/Article.php' => '3ba11a61d61b4144d0877d06dce574712c8bb82a542982687fef2e045801fe29',
    '/app/store/model/article/Category.php' => 'a8380f98eff4fd1ccbd46e7b0af49209d207b1a70299bfb06140e46f3966da00',
    '/app/common/model/Article.php' => '78b3a13c6cf1bc45cfb98fb42dcc0abce4aa4b5ae847f10036f2f316b4436ad6',
    '/app/common/model/article/Category.php' => '50ea22a80c59a283b71b4bf2f1b3471088032821b156f064c4e2bbc015a80c7d',
    '/cores/BaseModel.php' => '60925f69e87fa3e326fd5fef3b886bc8833dc70705543f444653f815b32ddec1',
    '/cores/BaseController.php' => '09057773e3abad90d7228e43fe45b09937d36baccf9b5f2d401eb6212c802621',
    '/cores/Request.php' => '7d8586387ec5b0867f3b376b632105e0f4d42e03f112e504c01f08c8ba059b8b',
    '/app/common.php' => 'c83bd472fdaa75afe201078504ef5a0a010f6d3535b9816c1f0de25b2ef4f66f',
    '/app/store/controller/Controller.php' => '73e52a9644398e9379e1f865242b015039398f308abeed70323120ba62c5db2d',
];
if (PHP_VERSION_ID < 70400 || PHP_VERSION_ID >= 70500 || is_link($root)) {
    throw new RuntimeException('Runtime/root drift');
}
foreach ($sourceHashes as $path => $sha) {
    if (!is_file($root . $path) || hash_file('sha256', $root . $path) !== $sha) {
        throw new RuntimeException('Business source drift');
    }
}

$stage = 'http-boundary';
if ($lane === 'B') {
    $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'));
    $local = parse_ini_file(
        '/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local',
        true, INI_SCANNER_RAW);
    if (!is_array($local) || !isset($local['DATABASE'])
        || strtolower((string)($local['DATABASE']['TYPE'] ?? '')) !== 'mysql'
        || (string)($local['DATABASE']['HOSTPORT'] ?? '') !== '3306'
        || $owner !== (string)$pid
        || strpos($ps, 'POC_TARGET_ROOT=/tmp/zhct-http-ai-macos.x2CCZX') === false
        || strpos($ps, '-S 127.0.0.1:' . $port) === false
        || strpos($ps, 'POC_DIALECT=mysql') === false
        || strpos($ps, 'POC_EXTERNAL_STUBS=1') === false) {
        throw new RuntimeException('B isolated worker drift');
    }
    foreach (['HOSTNAME' => 'HOST', 'HOSTPORT' => 'PORT', 'DATABASE' => 'SCHEMA',
        'USERNAME' => 'USER', 'PASSWORD' => 'PASSWORD'] as $key => $envKey) {
        $expected = (string)($local['DATABASE'][$key] ?? '');
        if ($expected === '' || strpos($ps, 'POC_AI_' . $envKey . '=' . $expected) === false) {
            throw new RuntimeException('B worker differs from .env.local');
        }
    }
    if (hash_file('sha256', $root . '/config/database.php')
            !== '8c46a6d9cd277f2950818d09162f639a62f135a5eb6553e6c61ce2c19167fd02'
        || hash_file('sha256', $root . '/config/cache.php')
            !== '98fa13484feb3db94bc4ebdd3de2f0b4ebc2c80d1cccb7a283a16f45eb9095a4'
        || hash_file('sha256', $root . '/public/index.php')
            !== '87ff41c14c0380cfba8cb99b02c8e440ad90f975e2e129dfb6e5657a36a7868d') {
        throw new RuntimeException('B isolation file drift');
    }
    $cfg = $local['DATABASE'];
    $connect = static function () use ($cfg): PDO {
        return 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, PDO::ATTR_TIMEOUT => 5]);
    };
    $tables = ['category' => 'yoshop_article_category',
        'article' => 'yoshop_article', 'file' => 'yoshop_upload_file'];
} else {
    $env = [];
    foreach (explode("\0", (string)file_get_contents('/proc/' . $pid . '/environ')) as $entry) {
        $parts = explode('=', $entry, 2);
        if (count($parts) === 2) $env[$parts[0]] = $parts[1];
    }
    $children = trim((string)@file_get_contents('/proc/' . $pid . '/task/' . $pid . '/children'));
    if (($env['POC_TARGET_ROOT'] ?? '') !== $root
        || ($env['POC_BASE_URL'] ?? '') !== $base
        || ($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'
        || $children !== '' || !in_array('dm', PDO::getAvailableDrivers(), true)) {
        throw new RuntimeException('C isolated worker drift');
    }
    $connect = static function () use ($env): PDO {
        $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, PDO::ATTR_TIMEOUT => 5]);
        if ((string)$db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")
                ->fetchColumn() !== 'AIZHCT_APP') {
            throw new RuntimeException('C effective schema drift');
        }
        return $db;
    };
    $tables = ['category' => 'YOSHOP_ARTICLE_CATEGORY',
        'article' => 'YOSHOP_ARTICLE', 'file' => 'YOSHOP_UPLOAD_FILE'];
}

$boundaryRaw = @file_get_contents($base . '/__poc_boundary');
$boundary = is_string($boundaryRaw) ? json_decode($boundaryRaw, true) : null;
if (($lane === 'B' && !is_string($boundaryRaw))
    || ($lane === 'C'
        && (!is_array($boundary) || ($boundary['external_stubs'] ?? null) !== true))) {
    throw new RuntimeException('HTTP boundary absent');
}

$db = $connect();
$rows = static function (PDO $pdo, string $sql, array $args = []): array {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($args);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
};
$count = static function (PDO $pdo, string $sql, array $args = []): int {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($args);
    return (int)$stmt->fetchColumn();
};

$stage = 'metadata-gate';
$required = [
    'category' => ['CATEGORY_ID', 'NAME', 'STATUS', 'SORT', 'STORE_ID', 'CREATE_TIME', 'UPDATE_TIME'],
    'article' => ['ARTICLE_ID', 'TITLE', 'SHOW_TYPE', 'CATEGORY_ID', 'IMAGE_ID', 'CONTENT',
        'SORT', 'STATUS', 'VIRTUAL_VIEWS', 'ACTUAL_VIEWS', 'IS_DELETE', 'STORE_ID',
        'CREATE_TIME', 'UPDATE_TIME'],
    'file' => ['FILE_ID', 'GROUP_ID', 'CHANNEL', 'STORAGE', 'DOMAIN', 'FILE_TYPE',
        'FILE_NAME', 'FILE_PATH', 'FILE_SIZE', 'FILE_EXT', 'COVER', 'UPLOADER_ID',
        'IS_RECYCLE', 'IS_DELETE', 'STORE_ID', 'CREATE_TIME', 'UPDATE_TIME'],
];
$metadata = [];
foreach ($required as $logical => $columns) {
    if ($dialect === 'mysql') {
        $stmt = $db->prepare('SELECT COLUMN_NAME,DATA_TYPE,COLUMN_TYPE,IS_NULLABLE,COLUMN_DEFAULT,EXTRA'
            . ' FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?'
            . ' ORDER BY ORDINAL_POSITION');
        $stmt->execute([$tables[$logical]]);
    } else {
        $stmt = $db->prepare('SELECT COLUMN_NAME,DATA_TYPE,DATA_LENGTH,DATA_PRECISION,DATA_SCALE,'
            . ' NULLABLE,DATA_DEFAULT FROM USER_TAB_COLUMNS WHERE TABLE_NAME=? ORDER BY COLUMN_ID');
        $stmt->execute([$tables[$logical]]);
    }
    $seen = [];
    foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
        $seen[] = strtoupper((string)$row['column_name']);
        $metadata[] = [$logical, $row];
    }
    foreach ($columns as $column) {
        if (!in_array($column, $seen, true)) throw new RuntimeException('Required column absent');
    }
}
if ($dialect === 'mysql') {
    $physical = array_values($tables);
    $marks = implode(',', array_fill(0, count($physical), '?'));
    $stmt = $db->prepare('SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS'
        . ' WHERE TRIGGER_SCHEMA=DATABASE() AND EVENT_OBJECT_TABLE IN (' . $marks . ')');
    $stmt->execute($physical);
    $triggers = (int)$stmt->fetchColumn();
    $stmt = $db->prepare('SELECT COUNT(*) FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS'
        . ' WHERE CONSTRAINT_SCHEMA=DATABASE() AND (TABLE_NAME IN (' . $marks . ')'
        . ' OR REFERENCED_TABLE_NAME IN (' . $marks . '))');
    $stmt->execute(array_merge($physical, $physical));
    $fks = (int)$stmt->fetchColumn();
} else {
    $triggers = (int)$db->query("SELECT COUNT(*) FROM USER_TRIGGERS WHERE TABLE_NAME IN "
        . "('YOSHOP_ARTICLE_CATEGORY','YOSHOP_ARTICLE','YOSHOP_UPLOAD_FILE')")->fetchColumn();
    $fks = (int)$db->query("SELECT COUNT(*) FROM USER_CONSTRAINTS WHERE CONSTRAINT_TYPE='R'"
        . " AND TABLE_NAME IN ('YOSHOP_ARTICLE_CATEGORY','YOSHOP_ARTICLE','YOSHOP_UPLOAD_FILE')")
        ->fetchColumn();
}
if ($triggers !== 0 || $fks !== 0) throw new RuntimeException('Unreviewed trigger or FK');

$owned = static function (PDO $pdo) use ($rows, $tables, $marker): array {
    return [
        'category' => $rows($pdo, 'SELECT * FROM ' . $tables['category']
            . ' WHERE STORE_ID=10001 AND NAME LIKE ?', [$marker . '%']),
        'article' => $rows($pdo, 'SELECT * FROM ' . $tables['article']
            . ' WHERE STORE_ID=10001 AND TITLE LIKE ?', [$marker . '%']),
        'file' => $rows($pdo, 'SELECT * FROM ' . $tables['file']
            . ' WHERE STORE_ID=10001 AND FILE_NAME=?', [$marker . '.png']),
    ];
};
$natural = static function (PDO $pdo) use ($rows, $tables): string {
    $category = $rows($pdo, 'SELECT * FROM ' . $tables['category']
        . " WHERE STORE_ID=10001 AND NAME NOT LIKE 'CDXARTICLE%' ORDER BY CATEGORY_ID");
    $article = $rows($pdo, 'SELECT * FROM ' . $tables['article']
        . " WHERE STORE_ID=10001 AND TITLE NOT LIKE 'CDXARTICLE%' ORDER BY ARTICLE_ID");
    $file = $rows($pdo, 'SELECT * FROM ' . $tables['file']
        . " WHERE STORE_ID=10001 AND FILE_NAME NOT LIKE 'CDXARTICLE%' ORDER BY FILE_ID");
    if (count($category) > 200 || count($article) > 200 || count($file) > 30000) {
        throw new RuntimeException('Natural snapshot cap exceeded');
    }
    return hash('sha256', json_encode([$category, $article, $file],
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
};
$originalNatural = $natural($db);
$oldCounts = array_map('count', $owned($db));

if ($mode === '--preflight') {
    $ready = $oldCounts === ['category' => 0, 'article' => 0, 'file' => 0];
    echo json_encode(['status' => $ready ? 'select_only_ready' : 'marker_stop',
        'lane' => $lane, 'metadata_sha' => hash('sha256', serialize($metadata)),
        'foreign_keys' => $fks, 'triggers' => $triggers,
        'old_marker_rows' => array_sum($oldCounts), 'natural_sha' => $originalNatural,
        'http_sent' => false, 'dml' => false]), PHP_EOL;
    exit($ready ? 0 : 2);
}

if ($mode === '--audit') {
    $stage = 'independent-audit';
    $journalPath = (string)getenv('CDX_AW6_JOURNAL');
    if (!preg_match('~^/tmp/cdx-article-write-[bc]-[a-f0-9]{12}/journal\.json$~D',
            $journalPath) || !is_file($journalPath) || is_link($journalPath)
        || is_link(dirname($journalPath))
        || (fileperms(dirname($journalPath)) & 0777) !== 0700
        || (fileperms($journalPath) & 0777) !== 0600) {
        throw new RuntimeException('Private journal invalid');
    }
    $saved = json_decode((string)file_get_contents($journalPath), true);
    if (!is_array($saved) || ($saved['lane'] ?? null) !== $lane
        || ($saved['marker'] ?? null) !== $marker
        || ($saved['phase'] ?? null) !== 'cleaned-zero'
        || ($saved['natural_sha'] ?? null) !== $originalNatural) {
        throw new RuntimeException('Audit ownership or natural source mismatch');
    }
    $fresh = $connect();
    if (array_sum(array_map('count', $owned($fresh))) !== 0
        || $natural($fresh) !== $originalNatural) {
        throw new RuntimeException('Audit zero mismatch');
    }
    unlink($journalPath);
    rmdir(dirname($journalPath));
    echo json_encode(['status' => 'independent_zero_pass', 'lane' => $lane,
        'marker_rows' => 0, 'natural_sha' => $originalNatural,
        'private_journal_removed' => true]), PHP_EOL;
    exit(0);
}

if ($oldCounts !== ['category' => 0, 'article' => 0, 'file' => 0]) {
    throw new RuntimeException('Old marker remains');
}

$journalDir = '/tmp/cdx-article-write-' . strtolower($lane) . '-'
    . strtolower(bin2hex(random_bytes(6)));
if (file_exists($journalDir) || !mkdir($journalDir, 0700) || !chmod($journalDir, 0700)) {
    throw new RuntimeException('Private journal directory unavailable');
}
$journalPath = $journalDir . '/journal.json';
$journal = ['lane' => $lane, 'marker' => $marker, 'phase' => 'before-write',
    'natural_sha' => $originalNatural, 'ids' => ['category' => 0, 'article' => 0, 'file' => 0]];
$saveJournal = static function () use (&$journal, $journalPath): void {
    $encoded = json_encode($journal, JSON_UNESCAPED_SLASHES);
    if (!is_string($encoded) || file_put_contents($journalPath, $encoded, LOCK_EX) !== strlen($encoded)
        || !chmod($journalPath, 0600)) {
        throw new RuntimeException('Private journal write failed');
    }
};
$saveJournal();

define('TP_ENV', $lane === 'B' ? 'local' : 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require_once $root . '/vendor/autoload.php';
(new \think\App($root))->setEnvName($lane === 'B' ? 'local' : 'poc')->initialize();
if (config('cache.default') !== 'file') throw new RuntimeException('File cache boundary absent');

$shape = null;
$shape = static function ($value) use (&$shape) {
    if (!is_array($value)) return gettype($value);
    if ($value === []) return [];
    $keys = array_keys($value);
    if ($keys === range(0, count($value) - 1)) return ['[]' => $shape($value[0])];
    $out = [];
    foreach ($value as $key => $item) $out[(string)$key] = $shape($item);
    ksort($out);
    return $out;
};
$request = static function (string $path, array $form, ?string $token) use ($base): array {
    $body = http_build_query($form, '', '&', PHP_QUERY_RFC3986);
    $headers = 'Content-Type: application/x-www-form-urlencoded' . "\r\n"
        . 'Content-Length: ' . strlen($body) . "\r\n"
        . ($token === null ? '' : 'Access-Token: ' . $token . "\r\n");
    $stream = @fopen($base . '/index.php?s=/store/' . $path, 'rb', false,
        stream_context_create(['http' => ['method' => 'POST', 'header' => $headers,
            'content' => $body, 'timeout' => 20, 'ignore_errors' => true,
            'follow_location' => 0]]));
    $raw = $stream === false ? false : stream_get_contents($stream, 1048577);
    $meta = is_resource($stream) ? stream_get_meta_data($stream) : [];
    $responseHeaders = $http_response_header ?? [];
    if (is_resource($stream)) fclose($stream);
    preg_match('/HTTP\/\S+\s+(\d+)/', (string)($responseHeaders[0] ?? ''), $match);
    $withinLimit = is_string($raw) && strlen($raw) <= 1048576;
    $json = $withinLimit ? json_decode($raw, true) : null;
    $jsonComplete = is_array($json) && json_last_error() === JSON_ERROR_NONE;
    return ['http' => (int)($match[1] ?? 0), 'json' => is_array($json) ? $json : null,
        'transport_complete' => $stream !== false && $raw !== false && isset($match[1])
            && empty($meta['timed_out']) && $withinLimit && $jsonComplete];
};

$ids = ['category' => 0, 'article' => 0, 'file' => 0];
$token = 'CDXARTICLEWRITE' . strtoupper(bin2hex(random_bytes(16)));
$tokenIssued = false;
$transportUncertain = false;
$failure = null;
$result = ['status' => 'FAIL', 'lane' => $lane, 'routes' => [],
    'rejections' => [], 'natural_sha' => $originalNatural,
    'no_real_file_or_oss' => true, 'journal' => $journalPath];

$record = static function (array $response) use ($shape): array {
    $json = $response['json'];
    return ['http' => $response['http'], 'status' => $json['status'] ?? null,
        'key_sha' => hash('sha256', serialize(array_keys($json))),
        'type_sha' => hash('sha256', serialize($shape($json))),'json_shape' => $shape($json)];
};
$assertSuccess = static function (array $response): void {
    if (!$response['transport_complete'] || !is_array($response['json'])
        || $response['http'] !== 200 || (int)($response['json']['status'] ?? -1) !== 200) {
        throw new RuntimeException('Expected HTTP business success absent');
    }
};
$assertReject = static function (array $response): void {
    if (!$response['transport_complete'] || !is_array($response['json'])
        || $response['http'] !== 200 || (int)($response['json']['status'] ?? 200) === 200) {
        throw new RuntimeException('Expected HTTP business rejection absent');
    }
};

$referenceScan = static function (PDO $pdo) use ($lane, $rows, $count, $tables, &$ids): array {
    $otherCategoryRefs = 0;
    if ($ids['category'] > 0) {
        $sql = 'SELECT COUNT(*) FROM ' . $tables['article'] . ' WHERE CATEGORY_ID=?';
        $args = [$ids['category']];
        if ($ids['article'] > 0) {
            $sql .= ' AND ARTICLE_ID<>?';
            $args[] = $ids['article'];
        }
        $otherCategoryRefs = $count($pdo, $sql, $args);
    }
    $otherFileRefs = 0;
    $examined = 0;
    if ($ids['file'] > 0) {
        $metaSql = $lane === 'B'
            ? "SELECT TABLE_NAME,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS"
                . " WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME LIKE 'yoshop_%'"
                . " AND COLUMN_NAME IN ('image_id','file_id')"
            : "SELECT TABLE_NAME,COLUMN_NAME FROM USER_TAB_COLUMNS"
                . " WHERE TABLE_NAME LIKE 'YOSHOP_%' AND COLUMN_NAME IN ('IMAGE_ID','FILE_ID')";
        foreach ($rows($pdo, $metaSql) as $ref) {
            $table = (string)$ref['table_name'];
            $column = (string)$ref['column_name'];
            if (!preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/D', $table)
                || !in_array(strtolower($column), ['image_id', 'file_id'], true)) {
                throw new RuntimeException('Reference metadata invalid');
            }
            if (strcasecmp($table, $tables['file']) === 0
                && strcasecmp($column, 'FILE_ID') === 0) continue;
            $sql = 'SELECT COUNT(*) FROM ' . $table . ' WHERE ' . $column . '=?';
            $args = [$ids['file']];
            if (strcasecmp($table, $tables['article']) === 0
                && strcasecmp($column, 'IMAGE_ID') === 0 && $ids['article'] > 0) {
                $sql .= ' AND ARTICLE_ID<>?';
                $args[] = $ids['article'];
            }
            $otherFileRefs += $count($pdo, $sql, $args);
            if (++$examined > 50) throw new RuntimeException('Reference scan cap exceeded');
        }
    }
    return ['other_articles_using_category' => $otherCategoryRefs,
        'other_rows_using_file' => $otherFileRefs, 'file_reference_columns_examined' => $examined];
};

try {
    $stage = 'file-metadata-insert';
    $column = static function (string $name) use ($lane): string {
        return $lane === 'C' ? '"' . $name . '"' : $name;
    };
    $fileColumns = implode(',', array_map($column, ['GROUP_ID','CHANNEL','STORAGE','DOMAIN',
        'FILE_TYPE','FILE_NAME','FILE_PATH','FILE_SIZE','FILE_EXT','COVER','UPLOADER_ID',
        'IS_RECYCLE','IS_DELETE','STORE_ID','CREATE_TIME','UPDATE_TIME']));
    $fileSql = 'INSERT INTO ' . $tables['file'] . ' (' . $fileColumns . ')'
        . ' VALUES (0,10,?,?,10,?,?,0,?,?,0,0,0,10001,?,?)';
    $db->prepare($fileSql);
    $now = time();
    $db->beginTransaction();
    $stmt = $db->prepare($fileSql);
    $stmt->execute(['local', '', $marker . '.png',
        'codex/' . strtolower($marker) . '.png', 'png', '', $now, $now]);
    $fileRows = $rows($db, 'SELECT * FROM ' . $tables['file']
        . ' WHERE STORE_ID=10001 AND FILE_NAME=?', [$marker . '.png']);
    if (count($fileRows) !== 1 || (string)$fileRows[0]['storage'] !== 'local') {
        throw new RuntimeException('File metadata insert/readback mismatch');
    }
    $ids['file'] = (int)$fileRows[0]['file_id'];
    if ($ids['file'] <= 0) throw new RuntimeException('File identity invalid');
    $db->commit();
    $journal['phase'] = 'file-metadata-committed';
    $journal['ids'] = $ids;
    $saveJournal();

    $stage = 'token-issue';
    if (!\think\facade\Cache::set($token, ['user' => [
            'store_user_id' => 2147480004, 'store_id' => 10001,
            'user_name' => $marker, 'real_name' => $marker, 'is_super' => 1,
        ], 'store_id' => 10001, 'is_login' => true], 300)
        || !\think\facade\Cache::has($token)) {
        throw new RuntimeException('Isolated File token unavailable');
    }
    $tokenIssued = true;

    $categoryAddForm = ['form' => ['name' => $marker . 'CAT', 'status' => 0, 'sort' => 901]];
    $stage = 'unauth-category-add';
    $response = $request('content.article.category/add', $categoryAddForm, null);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertReject($response);
    $result['rejections']['category.add.unauth'] = true;

    $stage = 'category-add';
    $response = $request('content.article.category/add', $categoryAddForm, $token);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertSuccess($response);
    $categoryRows = $rows($connect(), 'SELECT * FROM ' . $tables['category']
        . ' WHERE STORE_ID=10001 AND NAME=?', [$marker . 'CAT']);
    if (count($categoryRows) !== 1 || (int)$categoryRows[0]['status'] !== 0
        || (int)$categoryRows[0]['sort'] !== 901) throw new RuntimeException('Category add readback mismatch');
    $ids['category'] = (int)$categoryRows[0]['category_id'];
    $result['routes']['category.add'] = array_merge($record($response), ['db_write' => true]);
    $journal['phase'] = 'category-added'; $journal['ids'] = $ids; $saveJournal();

    $categoryEditForm = ['categoryId' => $ids['category'],
        'form' => ['name' => $marker . 'CATEDIT', 'status' => 0, 'sort' => 902]];
    $stage = 'unauth-category-edit';
    $response = $request('content.article.category/edit', $categoryEditForm, null);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertReject($response);
    $result['rejections']['category.edit.unauth'] = true;

    $stage = 'category-edit';
    $response = $request('content.article.category/edit', $categoryEditForm, $token);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertSuccess($response);
    $categoryRows = $rows($connect(), 'SELECT * FROM ' . $tables['category']
        . ' WHERE CATEGORY_ID=? AND STORE_ID=10001', [$ids['category']]);
    if (count($categoryRows) !== 1 || (string)$categoryRows[0]['name'] !== $marker . 'CATEDIT'
        || (int)$categoryRows[0]['status'] !== 0 || (int)$categoryRows[0]['sort'] !== 902) {
        throw new RuntimeException('Category edit readback mismatch');
    }
    $result['routes']['category.edit'] = array_merge($record($response), ['db_write' => true]);

    $articleAddData = ['title' => $marker . 'ARTICLE', 'show_type' => 10,
        'category_id' => $ids['category'], 'image_id' => $ids['file'],
        'content' => '<p>' . $marker . ' 中文内容</p>', 'virtual_views' => 7,
        'status' => 0, 'sort' => 903];
    $stage = 'unauth-article-add';
    $response = $request('content.article/add', ['form' => $articleAddData], null);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertReject($response);
    $result['rejections']['article.add.unauth'] = true;

    $stage = 'article-add-missing-image-reject';
    $invalid = $articleAddData; unset($invalid['image_id']);
    $response = $request('content.article/add', ['form' => $invalid], $token);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertReject($response);
    if ($count($connect(), 'SELECT COUNT(*) FROM ' . $tables['article']
            . ' WHERE STORE_ID=10001 AND TITLE LIKE ?', [$marker . '%']) !== 0) {
        throw new RuntimeException('Rejected article add mutated DB');
    }
    $result['rejections']['article.add.missing_image'] = true;

    $stage = 'article-add';
    $response = $request('content.article/add', ['form' => $articleAddData], $token);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertSuccess($response);
    $articleRows = $rows($connect(), 'SELECT * FROM ' . $tables['article']
        . ' WHERE STORE_ID=10001 AND TITLE=?', [$marker . 'ARTICLE']);
    if (count($articleRows) !== 1 || (int)$articleRows[0]['category_id'] !== $ids['category']
        || (int)$articleRows[0]['image_id'] !== $ids['file']
        || (int)$articleRows[0]['show_type'] !== 10 || (int)$articleRows[0]['status'] !== 0
        || (int)$articleRows[0]['sort'] !== 903 || (int)$articleRows[0]['virtual_views'] !== 7
        || (int)$articleRows[0]['is_delete'] !== 0
        || (string)$articleRows[0]['content']
            !== htmlspecialchars($articleAddData['content'], ENT_COMPAT)) {
        throw new RuntimeException('Article add readback mismatch');
    }
    $ids['article'] = (int)$articleRows[0]['article_id'];
    $result['routes']['article.add'] = array_merge($record($response), ['db_write' => true]);
    $journal['phase'] = 'article-added'; $journal['ids'] = $ids; $saveJournal();

    $categoryDeleteForm = ['categoryId' => $ids['category']];
    $stage = 'unauth-category-delete';
    $response = $request('content.article.category/delete', $categoryDeleteForm, null);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertReject($response);
    $result['rejections']['category.delete.unauth'] = true;

    $stage = 'category-delete-active-article-reject';
    $response = $request('content.article.category/delete', $categoryDeleteForm, $token);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertReject($response);
    if ($count($connect(), 'SELECT COUNT(*) FROM ' . $tables['category']
            . ' WHERE CATEGORY_ID=? AND STORE_ID=10001', [$ids['category']]) !== 1) {
        throw new RuntimeException('Rejected category delete mutated DB');
    }
    $result['rejections']['category.delete.active_article'] = true;

    $articleEditData = ['title' => $marker . 'ARTICLEEDIT', 'show_type' => 20,
        'category_id' => $ids['category'], 'image_id' => $ids['file'],
        'content' => '<p>' . $marker . ' 更新内容</p>', 'virtual_views' => 9,
        'status' => 0, 'sort' => 904];
    $articleEditForm = ['articleId' => $ids['article'], 'form' => $articleEditData];
    $stage = 'unauth-article-edit';
    $response = $request('content.article/edit', $articleEditForm, null);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertReject($response);
    $result['rejections']['article.edit.unauth'] = true;

    $stage = 'article-edit';
    $response = $request('content.article/edit', $articleEditForm, $token);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertSuccess($response);
    $articleRows = $rows($connect(), 'SELECT * FROM ' . $tables['article']
        . ' WHERE ARTICLE_ID=? AND STORE_ID=10001', [$ids['article']]);
    if (count($articleRows) !== 1 || (string)$articleRows[0]['title'] !== $marker . 'ARTICLEEDIT'
        || (int)$articleRows[0]['show_type'] !== 20 || (int)$articleRows[0]['sort'] !== 904
        || (int)$articleRows[0]['virtual_views'] !== 9
        || (string)$articleRows[0]['content']
            !== htmlspecialchars($articleEditData['content'], ENT_COMPAT)) {
        throw new RuntimeException('Article edit readback mismatch');
    }
    $result['routes']['article.edit'] = array_merge($record($response), ['db_write' => true]);

    $articleDeleteForm = ['articleId' => $ids['article']];
    $stage = 'unauth-article-delete';
    $response = $request('content.article/delete', $articleDeleteForm, null);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertReject($response);
    $result['rejections']['article.delete.unauth'] = true;

    $stage = 'article-delete';
    $response = $request('content.article/delete', $articleDeleteForm, $token);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertSuccess($response);
    $articleRows = $rows($connect(), 'SELECT * FROM ' . $tables['article']
        . ' WHERE ARTICLE_ID=? AND STORE_ID=10001', [$ids['article']]);
    if (count($articleRows) !== 1 || (int)$articleRows[0]['is_delete'] !== 1) {
        throw new RuntimeException('Article soft delete readback mismatch');
    }
    $result['routes']['article.delete'] = array_merge($record($response),
        ['db_write' => true, 'soft_deleted_row' => 1]);

    $stage = 'category-delete';
    $response = $request('content.article.category/delete', $categoryDeleteForm, $token);
    if (!$response['transport_complete']) $transportUncertain = true;
    $assertSuccess($response);
    if ($count($connect(), 'SELECT COUNT(*) FROM ' . $tables['category']
            . ' WHERE CATEGORY_ID=? AND STORE_ID=10001', [$ids['category']]) !== 0) {
        throw new RuntimeException('Category delete readback mismatch');
    }
    $result['routes']['category.delete'] = array_merge($record($response),
        ['db_write' => true, 'physically_deleted_row' => 1]);
    $result['status'] = 'HTTP_PASS_PENDING_CLEANUP';
} catch (Throwable $e) {
    if ($db->inTransaction()) $db->rollBack();
    $failure = ['stage' => $stage, 'class' => get_class($e),
        'code' => (string)$e->getCode(), 'message_sha' => hash('sha256', $e->getMessage())];
}

if ($tokenIssued) {
    \think\facade\Cache::delete($token);
    $result['token_deleted'] = !\think\facade\Cache::has($token);
} else {
    $result['token_deleted'] = true;
}

if ($transportUncertain) {
    $journal['phase'] = 'transport-uncertain-retain';
    $journal['ids'] = $ids;
    $saveJournal();
    echo json_encode(['status' => 'transport_uncertain_retain_fixture',
        'lane' => $lane, 'journal' => $journalPath]), PHP_EOL;
    exit(3);
}

$stage = 'cleanup';
$clean = $connect();
$selfRows = $owned($clean);
foreach ($selfRows as $name => $items) {
    if (count($items) > 1) throw new RuntimeException('Ambiguous owned cleanup');
}
if ($ids['file'] === 0 && count($selfRows['file']) === 1) $ids['file'] = (int)$selfRows['file'][0]['file_id'];
if ($ids['article'] === 0 && count($selfRows['article']) === 1) $ids['article'] = (int)$selfRows['article'][0]['article_id'];
if ($ids['category'] === 0 && count($selfRows['category']) === 1) $ids['category'] = (int)$selfRows['category'][0]['category_id'];
$refs = $referenceScan($clean);
if ($refs['other_articles_using_category'] !== 0 || $refs['other_rows_using_file'] !== 0) {
    $journal['phase'] = 'unexpected-reference-retain';
    $journal['ids'] = $ids;
    $saveJournal();
    echo json_encode(['status' => 'unexpected_reference_retain_fixture', 'lane' => $lane,
        'journal' => $journalPath, 'reference_sha' => hash('sha256', serialize($refs))]), PHP_EOL;
    exit(4);
}
$clean->beginTransaction();
try {
    if ($ids['article'] > 0) {
        $stmt = $clean->prepare('DELETE FROM ' . $tables['article']
            . ' WHERE ARTICLE_ID=? AND STORE_ID=10001 AND TITLE LIKE ?');
        $stmt->execute([$ids['article'], $marker . '%']);
        if ($stmt->rowCount() > 1) throw new RuntimeException('Article cleanup scope mismatch');
    }
    if ($ids['category'] > 0) {
        $stmt = $clean->prepare('DELETE FROM ' . $tables['category']
            . ' WHERE CATEGORY_ID=? AND STORE_ID=10001 AND NAME LIKE ?');
        $stmt->execute([$ids['category'], $marker . '%']);
        if ($stmt->rowCount() > 1) throw new RuntimeException('Category cleanup scope mismatch');
    }
    if ($ids['file'] > 0) {
        $stmt = $clean->prepare('DELETE FROM ' . $tables['file']
            . ' WHERE FILE_ID=? AND STORE_ID=10001 AND FILE_NAME=? AND STORAGE=?');
        $stmt->execute([$ids['file'], $marker . '.png', 'local']);
        if ($stmt->rowCount() > 1) throw new RuntimeException('File cleanup scope mismatch');
    }
    $clean->commit();
} catch (Throwable $e) {
    if ($clean->inTransaction()) $clean->rollBack();
    $journal['phase'] = 'cleanup-failed'; $journal['ids'] = $ids; $saveJournal();
    throw $e;
}
$fresh = $connect();
$cleanupZero = array_sum(array_map('count', $owned($fresh))) === 0;
$naturalUnchanged = $natural($fresh) === $originalNatural;
if (!$cleanupZero || !$naturalUnchanged) throw new RuntimeException('Cleanup/natural source mismatch');
$journal['phase'] = 'cleaned-zero'; $journal['ids'] = $ids; $saveJournal();
$result['cleanup'] = ['marker_rows' => 0, 'natural_unchanged' => true,
    'reference_scan' => $refs, 'journal' => $journalPath];
if ($failure === null && count($result['routes']) === 6 && count($result['rejections']) === 8
    && ($result['token_deleted'] ?? false)) {
    $result['status'] = 'HTTP_PASS_CLEANED_PENDING_AUDIT';
    echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
$result['status'] = 'HTTP_FAIL_CLEANED';
$result['failure'] = $failure;
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
exit(2);
