<?php
declare(strict_types=1);

$phase = (string)($argv[1] ?? '');
$lane = (string)($argv[2] ?? '');
$marker = (string)($argv[3] ?? '');
if (!in_array($phase, ['--run', '--audit'], true) || !in_array($lane, ['B', 'C'], true)
    || !preg_match('/^CDXPUB[A-F0-9]{10}$/D', $marker)
    || ($phase === '--run' && getenv('CDX_PUBLIC_ARTICLE_RUN') !== 'OWNED_TWO_ROWS_V1')) {
    fwrite(STDERR, "DENIED business_http=0 business_dml=0\n");
    exit(78);
}
if ($lane === 'B') {
    $config = parse_ini_file('/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local', true, INI_SCANNER_RAW)['DATABASE'] ?? null;
    if (!is_array($config) || strtolower((string)($config['TYPE'] ?? '')) !== 'mysql'
        || (string)($config['HOSTPORT'] ?? '') !== '3306'
        || hash('sha256', (string)($config['HOSTNAME'] ?? '')) !== '47e8be48b0cfd56dcdb9d9d426e6637b245f340b4efad4cd2f084ec5851ecc8e'
        || hash('sha256', (string)($config['DATABASE'] ?? '')) !== 'cf7756b3bb52cf72f625689134015b877fdab3ad8eb3bb2ddd962bb35f6efd66') {
        throw new RuntimeException('B code-local AI MySQL identity drift');
    }
    $db = new PDO('mysql:host=' . $config['HOSTNAME'] . ';port=3306;dbname=' . $config['DATABASE']
        . ';charset=' . $config['CHARSET'], $config['USERNAME'], $config['PASSWORD'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
    $port = 18101;
} else {
    if (getenv('POC_DIALECT') !== 'dm' || getenv('POC_AI_HOST') !== '127.0.0.1'
        || getenv('POC_AI_PORT') !== '5238' || getenv('POC_AI_SCHEMA') !== 'AIZHCT_APP'
        || getenv('POC_AI_USER') !== 'AIZHCT_APP') {
        throw new RuntimeException('C isolated AI DM identity drift');
    }
    $db = new PDO('dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP',
        getenv('POC_AI_USER'), getenv('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');
    }
    $port = 18102;
}
$one = static function (string $sql, array $args = []) use ($db) {
    $query = $db->prepare($sql);
    $query->execute($args);
    return $query->fetch(PDO::FETCH_ASSOC);
};
$all = static function (string $sql, array $args = []) use ($db): array {
    $query = $db->prepare($sql);
    $query->execute($args);
    return $query->fetchAll(PDO::FETCH_ASSOC);
};
$categoryName = $marker . 'CAT';
$articleTitle = $marker . 'ARTICLE';
$owned = static function () use ($all, $categoryName, $articleTitle): array {
    return [
        'category' => $all('SELECT CATEGORY_ID FROM yoshop_article_category'
            . ' WHERE STORE_ID=10001 AND NAME=?', [$categoryName]),
        'article' => $all('SELECT ARTICLE_ID FROM yoshop_article'
            . ' WHERE STORE_ID=10001 AND TITLE=?', [$articleTitle]),
    ];
};
$ownedBefore = $owned();
if ($phase === '--audit') {
    echo json_encode(['lane' => $lane, 'phase' => 'audit', 'marker' => $marker,
        'owned_category_count' => count($ownedBefore['category']),
        'owned_article_count' => count($ownedBefore['article']),
        'business_http' => 0, 'business_dml' => 0]), PHP_EOL;
    exit(count($ownedBefore['category']) + count($ownedBefore['article']) === 0 ? 0 : 2);
}
if (count($ownedBefore['category']) + count($ownedBefore['article']) !== 0) {
    throw new RuntimeException('self marker already exists');
}
// No trigger/FK on these two tables was found in the earlier vetted article fixture.
$insertCategory = $db->prepare('INSERT INTO yoshop_article_category'
    . ' (NAME,STATUS,SORT,STORE_ID,CREATE_TIME,UPDATE_TIME) VALUES (?,1,0,10001,?,?)');
$insertArticle = $db->prepare('INSERT INTO yoshop_article'
    . ' (TITLE,SHOW_TYPE,CATEGORY_ID,IMAGE_ID,CONTENT,SORT,STATUS,VIRTUAL_VIEWS,'
    . ' ACTUAL_VIEWS,IS_DELETE,STORE_ID,CREATE_TIME,UPDATE_TIME)'
    . ' VALUES (?,10,?,0,?,0,1,0,0,0,10001,?,?)');
$http = static function (string $path) use ($port): array {
    $context = stream_context_create(['http' => [
        'method' => 'GET', 'header' => "platform: H5\r\n", 'ignore_errors' => true,
        'timeout' => 15, 'follow_location' => 0,
    ]]);
    $handle = @fopen('http://127.0.0.1:' . $port . $path, 'rb', false, $context);
    if (!is_resource($handle)) throw new RuntimeException('isolated AI HTTP unavailable');
    $raw = stream_get_contents($handle, 1048577);
    fclose($handle);
    if (!is_string($raw) || strlen($raw) > 1048576) throw new RuntimeException('HTTP response cap');
    $body = json_decode($raw, true);
    if (!is_array($body) || (int)($body['status'] ?? 0) !== 200) {
        throw new RuntimeException('HTTP business response failed');
    }
    return $body;
};
$naturalSnapshot = static function () use ($all): string {
    $categories = $all('SELECT * FROM yoshop_article_category'
        . " WHERE STORE_ID=10001 AND NAME NOT LIKE 'CDXPUB%' ORDER BY CATEGORY_ID");
    $articles = $all('SELECT * FROM yoshop_article'
        . " WHERE STORE_ID=10001 AND TITLE NOT LIKE 'CDXPUB%' ORDER BY ARTICLE_ID");
    return hash('sha256', json_encode([$categories, $articles], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
};
$naturalBefore = $naturalSnapshot();
$categoryId = 0;
$articleId = 0;
$committed = false;
$result = null;
$error = null;
try {
    $now = time();
    $db->beginTransaction();
    $insertCategory->execute([$categoryName, $now, $now]);
    $category = $one('SELECT CATEGORY_ID FROM yoshop_article_category'
        . ' WHERE STORE_ID=10001 AND NAME=?', [$categoryName]);
    if (!is_array($category)) throw new RuntimeException('inserted category not found');
    $categoryId = (int)array_change_key_case($category, CASE_LOWER)['category_id'];
    $insertArticle->execute([$articleTitle, $categoryId, '<p>CDX public article compatibility</p>', $now, $now]);
    $article = $one('SELECT ARTICLE_ID FROM yoshop_article'
        . ' WHERE STORE_ID=10001 AND TITLE=?', [$articleTitle]);
    if (!is_array($article)) throw new RuntimeException('inserted article not found');
    $articleId = (int)array_change_key_case($article, CASE_LOWER)['article_id'];
    $db->commit();
    $committed = true;
    $categoryBody = $http('/api/article.Category/list');
    $categoryList = $categoryBody['data']['list'] ?? null;
    if (!is_array($categoryList)) throw new RuntimeException('category list shape');
    $ownedCategory = array_values(array_filter($categoryList, static function ($row) use ($categoryId): bool {
        return (int)($row['category_id'] ?? 0) === $categoryId;
    }));
    if (count($ownedCategory) !== 1 || (string)$ownedCategory[0]['name'] !== $categoryName) {
        throw new RuntimeException('category response differs from own row');
    }
    $articleBody = $http('/api/Article/list?categoryId=' . $categoryId);
    $articleList = $articleBody['data']['list'] ?? null;
    $items = is_array($articleList) ? ($articleList['data'] ?? null) : null;
    if (!is_array($items) || (int)($articleList['total'] ?? -1) !== 1
        || count($items) !== 1 || (int)($items[0]['article_id'] ?? 0) !== $articleId
        || (string)($items[0]['title'] ?? '') !== $articleTitle) {
        throw new RuntimeException('article list differs from own row');
    }
    $detailBody = $http('/api/Article/detail?articleId=' . $articleId);
    $detail = $detailBody['data']['detail'] ?? null;
    if (!is_array($detail) || (int)($detail['article_id'] ?? 0) !== $articleId
        || (string)($detail['title'] ?? '') !== $articleTitle) {
        throw new RuntimeException('article detail differs from own row');
    }
    $views = $one('SELECT ACTUAL_VIEWS FROM yoshop_article'
        . ' WHERE ARTICLE_ID=? AND TITLE=?', [$articleId, $articleTitle]);
    if (!is_array($views) || (int)array_change_key_case($views, CASE_LOWER)['actual_views'] !== 1) {
        throw new RuntimeException('detail actual_views did not write back exactly once');
    }
    $typeMap = static function (array $row): array {
        $map = [];
        foreach ($row as $key => $value) $map[$key] = gettype($value);
        ksort($map);
        return $map;
    };
    $typeGraph = static function ($value) use (&$typeGraph) {
        if (!is_array($value)) return gettype($value);
        if (!$value) return [];
        if (array_keys($value) === range(0, count($value) - 1)) {
            return array_map($typeGraph, $value);
        }
        ksort($value);
        return array_map($typeGraph, $value);
    };
    $result = ['lane' => $lane, 'marker' => $marker, 'http_status' => 200,
        'category_own_rows' => count($ownedCategory), 'filtered_article_total' => 1,
        'detail_views_after' => 1,
        'category_type_sha256' => hash('sha256', json_encode($typeMap($ownedCategory[0]))),
        'article_type_sha256' => hash('sha256', json_encode($typeMap($items[0]))),
        'detail_type_sha256' => hash('sha256', json_encode($typeMap($detail))),
        'category_recursive_type_sha256' => hash('sha256', json_encode($typeGraph($ownedCategory[0]))),
        'article_recursive_type_sha256' => hash('sha256', json_encode($typeGraph($items[0]))),
        'detail_recursive_type_sha256' => hash('sha256', json_encode($typeGraph($detail))),
        'business_dml' => 'two-owned-inserts-one-detail-view-increment-followed-by-cleanup'];
} catch (Throwable $caught) {
    $error = $caught;
} finally {
    if ($db->inTransaction()) $db->rollBack();
    if ($committed) {
        $db->beginTransaction();
        try {
            if ($articleId > 0) {
                $delete = $db->prepare('DELETE FROM yoshop_article'
                    . ' WHERE ARTICLE_ID=? AND TITLE=? AND STORE_ID=10001');
                $delete->execute([$articleId, $articleTitle]);
                if ($one('SELECT ARTICLE_ID FROM yoshop_article WHERE ARTICLE_ID=?', [$articleId])) {
                    throw new RuntimeException('owned article cleanup failed');
                }
            }
            if ($categoryId > 0) {
                $refs = $one('SELECT COUNT(*) AS N FROM yoshop_article WHERE CATEGORY_ID=?', [$categoryId]);
                if ((int)array_change_key_case($refs, CASE_LOWER)['n'] !== 0) {
                    throw new RuntimeException('category has non-owned references');
                }
                $delete = $db->prepare('DELETE FROM yoshop_article_category'
                    . ' WHERE CATEGORY_ID=? AND NAME=? AND STORE_ID=10001');
                $delete->execute([$categoryId, $categoryName]);
                if ($one('SELECT CATEGORY_ID FROM yoshop_article_category WHERE CATEGORY_ID=?', [$categoryId])) {
                    throw new RuntimeException('owned category cleanup failed');
                }
            }
            $db->commit();
        } catch (Throwable $cleanupError) {
            $db->rollBack();
            throw $cleanupError;
        }
    }
}
$ownedAfter = $owned();
if (count($ownedAfter['category']) + count($ownedAfter['article']) !== 0
    || $naturalBefore !== $naturalSnapshot()) {
    throw new RuntimeException('independent own/natural postcheck failed');
}
if ($error !== null) throw $error;
$result['cleanup_verified_zero'] = true;
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
