<?php
declare(strict_types=1);

$phase = (string)($argv[1] ?? '');
$lane = (string)($argv[2] ?? '');
$marker = (string)($argv[3] ?? '');
if (!in_array($phase, ['--audit', '--run'], true) || !in_array($lane, ['B', 'C'], true)
    || !preg_match('/^CDXHIS[A-F0-9]{12}$/D', $marker)
    || ($phase === '--run' && getenv('CDX_ARTICLE_SEARCH_RUN') !== 'ONE_OWN_USER_TWO_SEARCHES_V1')) {
    fwrite(STDERR, "DENIED business_http=0 business_dml=0\n"); exit(78);
}
if ($lane === 'B') {
    $root = '/tmp/zhct-http-store-mysql.dQTTBP';
    $c = include $root . '/application/config/local/database.php';
    if (!is_array($c) || ($c['database'] ?? '') !== 'zhct'
        || hash('sha256', (string)($c['hostname'] ?? ''))
            !== '47e8be48b0cfd56dcdb9d9d426e6637b245f340b4efad4cd2f084ec5851ecc8e') {
        throw new RuntimeException('B Store code-local MySQL identity drift');
    }
    $db = new PDO('mysql:host=' . $c['hostname'] . ';port=3306;dbname=zhct;charset=utf8',
        $c['username'], $c['password'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
    $port = 18081;
} else {
    $root = '/tmp/zhct-dm-poc-20260928.fcpiiN/store';
    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 DM 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 effective Store schema drift');
    }
    $port = 18080;
}
$select = static function (string $sql, array $args = []) use ($db): array {
    $stmt = $db->prepare($sql); $stmt->execute($args);
    return array_map(static function (array $r): array { return array_change_key_case($r, CASE_LOWER); },
        $stmt->fetchAll(PDO::FETCH_ASSOC));
};
$own = static function () use ($select, $marker): array {
    return [
        $select('SELECT id,uuid FROM ydy_user WHERE uuid=? AND account=? AND create_by=?',
            [$marker, $marker, 'CODEX']),
        $select('SELECT id,keyword,user_uuid,type,create_by FROM ydy_history_search'
            . ' WHERE user_uuid=? AND type=2', [$marker])
    ];
};
$before = $own();
if ($phase === '--audit') {
    echo json_encode(['lane' => $lane, 'phase' => 'audit',
        'owned_user_history_counts' => [count($before[0]), count($before[1])],
        'business_http' => 0, 'business_dml' => 0]), PHP_EOL;
    exit($before === [[], []] ? 0 : 2);
}
if ($before !== [[], []]) throw new RuntimeException('owned article-search marker already present');
$articles = $select('SELECT uuid,title,article_cate_uuid FROM ydy_article WHERE status=1 ORDER BY id');
$candidate = null;
foreach ($articles as $row) {
    $title = trim((string)$row['title']);
    if ($title !== '' && strlen($title) <= 90) { $candidate = $row; break; }
}
if ($candidate === null) throw new RuntimeException('no bounded positive article');
$keyword = (string)$candidate['title'];
require_once $root . '/application/base/Security.php';
$http = static function (string $route, array $form, string $token) use ($port): array {
    if (!in_array($route, ['lists', 'getHistoryKeyword'], true)) {
        throw new RuntimeException('unsupported mobile article route');
    }
    $ctx = stream_context_create(['http' => ['method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\ntoken: {$token}\r\n",
        'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986),
        'ignore_errors' => true, 'timeout' => 25, 'follow_location' => 0]]);
    $h = @fopen('http://127.0.0.1:' . $port . '/m/article/' . $route, 'rb', false, $ctx);
    if (!is_resource($h)) throw new RuntimeException('isolated mobile article HTTP unavailable');
    $raw = stream_get_contents($h, 4194305); fclose($h);
    if (!is_string($raw) || strlen($raw) > 4194304) throw new RuntimeException('article-search HTTP cap');
    $body = json_decode($raw, true);
    if (!is_array($body) || (int)($body['code'] ?? -1) !== 0) {
        throw new RuntimeException('article-search business failure route=' . $route
            . ' code=' . (string)($body['code'] ?? 'nonjson'));
    }
    return $body;
};
$error = null; $out = [];
try {
    $stmt = $db->prepare('INSERT INTO ydy_user'
        . ' (uuid,account,pwd,salt,name,type,create_time,create_by,is_delete)'
        . ' VALUES (?,?,?,?,?,?,?,?,?)');
    $stmt->execute([$marker, $marker, 'DISABLED', strtoupper(bin2hex(random_bytes(8))),
        $marker, 0, date('Y-m-d H:i:s'), 'CODEX', 0]);
    $users = $own()[0];
    if (count($users) !== 1) throw new RuntimeException('own test actor absent');
    $token = \app\base\Security::encrypt(implode("\t",
        [(string)$users[0]['id'], $marker, $marker, '0', '', '', $marker]));
    if (!is_string($token) || $token === '') throw new RuntimeException('own actor token absent');
    $listTypes = [];
    for ($attempt = 1; $attempt <= 2; $attempt++) {
        $response = $http('lists', ['keyword' => $keyword], $token);
        $tree = $response['data'] ?? null;
        if (!is_array($tree) || !$tree) throw new RuntimeException('keyword article tree empty');
        $matches = [];
        $walk = static function (array $nodes) use (&$walk, &$matches, $candidate): void {
            foreach ($nodes as $node) {
                if (!is_array($node)) throw new RuntimeException('article tree node malformed');
                if ((string)($node['uuid'] ?? '') === (string)$candidate['uuid']
                    && isset($node['title'])) $matches[] = $node;
                if (isset($node['children']) && is_array($node['children'])) $walk($node['children']);
            }
        };
        $walk($tree);
        if (count($matches) !== 1 || (string)($matches[0]['title'] ?? '') !== $keyword) {
            throw new RuntimeException('keyword result lacks original article');
        }
        $history = $own()[1];
        if (count($history) !== 1 || (string)$history[0]['keyword'] !== $keyword
            || (string)$history[0]['create_by'] !== $marker) {
            throw new RuntimeException('keyword history original row missing/retry duplicate');
        }
        $types = [];
        foreach ($matches[0] as $field => $value) {
            if ($field !== 'children') $types[$field] = gettype($value);
        }
        ksort($types); $listTypes[] = hash('sha256', json_encode($types));
    }
    $historyBody = $http('getHistoryKeyword', [], $token)['data'] ?? null;
    if (!is_array($historyBody) || !is_array($historyBody['history_keyword'] ?? null)
        || count($historyBody['history_keyword']) !== 1
        || (string)$historyBody['history_keyword'][0] !== $keyword
        || !is_array($historyBody['hot_keyword'] ?? null)) {
        throw new RuntimeException('history HTTP differs from original row');
    }
    $out = ['lane' => $lane, 'business_http_ok' => 3,
        'source_article_uuid_sha12' => substr(hash('sha256', (string)$candidate['uuid']), 0, 12),
        'same_keyword_row_after_two_searches' => true,
        'selected_article_type_sha' => $listTypes,
        'history_keyword_type' => gettype($historyBody['history_keyword'][0]),
        'hot_keyword_count' => count($historyBody['hot_keyword'])];
} catch (Throwable $caught) { $error = $caught; }
finally {
    foreach ($own()[1] as $r) {
        $stmt = $db->prepare('DELETE FROM ydy_history_search WHERE id=? AND user_uuid=? AND type=2');
        $stmt->execute([(int)$r['id'], $marker]);
    }
    foreach ($own()[0] as $r) {
        $stmt = $db->prepare('DELETE FROM ydy_user WHERE id=? AND uuid=? AND account=? AND create_by=?');
        $stmt->execute([(int)$r['id'], $marker, $marker, 'CODEX']);
    }
}
if ($own() !== [[], []]) throw new RuntimeException('owned search actor/history cleanup failed');
if ($error !== null) throw $error;
$out['owned_user_history_cleanup_zero'] = true;
echo json_encode($out, JSON_UNESCAPED_SLASHES), PHP_EOL;
