<?php

declare(strict_types=1);

// Approved non-production DM Store only. All business rows are new CDXPAR fixtures.
$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/Station.php')
    || !is_file($root . '/application/p/controller/Archive.php')
    || !preg_match('~^http://127\.0\.0\.1:[0-9]+$~', $base)
    || !ctype_digit($runtimePid)) {
    throw new RuntimeException('Approved isolated Store DM runtime missing');
}
$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('Isolated HTTP boundary absent');
}
$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 {
    $statement = $db->prepare($sql);
    $statement->execute($args);
    return $statement->fetchAll(PDO::FETCH_ASSOC);
};
$one = static function (string $sql, array $args = []) use ($rows): array {
    return $rows($sql, $args)[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);
    $headersOut = $http_response_header ?? [];
    preg_match('/HTTP\/\S+\s+(\d+)/', (string)($headersOut[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, ?string $message = null): bool {
    return $response['http'] === 200 && is_array($response['json'])
        && (int)($response['json']['code'] ?? -1) === $businessCode
        && ($message === null || (string)($response['json']['message'] ?? '') === $message);
};

$required = [
    'YDY_STATION' => ['ID', 'UUID', 'NAME', 'STATUS', 'CREATE_BY'],
    'YDY_ARCHIVE' => ['ID', 'UUID', 'NAME', 'ORDER_NO', 'STATUS', 'LINK', 'IS_SHOW', 'CREATE_BY'],
    'YDY_STATION_ARCHIVE' => ['ID', 'STATION_UUID', 'ARCHIVE_UUID', 'CREATE_BY'],
    'YDY_STAFF' => ['UUID', 'STATION_UUID'],
    'YDY_USER' => ['ID', 'UUID', 'ACCOUNT', 'NAME', 'TYPE'],
];
$metadata = [];
foreach ($required as $name => $fields) {
    $columns = $rows('SELECT COLUMN_NAME,NULLABLE,DATA_DEFAULT FROM USER_TAB_COLUMNS WHERE TABLE_NAME=?', [$name]);
    $assert(count(array_diff($fields, array_column($columns, 'column_name'))) === 0,
        'Required DM relation table columns absent: ' . $name);
    $metadata[$name] = [
        'columns' => count($columns),
        'indexes' => $count('SELECT COUNT(*) N FROM USER_INDEXES WHERE TABLE_NAME=?', [$name]),
        'foreign_keys' => $count('SELECT COUNT(*) N FROM USER_CONSTRAINTS'
            . ' WHERE TABLE_NAME=? AND CONSTRAINT_TYPE=?', [$name, 'R']),
        'triggers' => $count('SELECT COUNT(*) N FROM USER_TRIGGERS WHERE TABLE_NAME=?', [$name]),
    ];
}
$archiveDefaults = [];
foreach ($rows('SELECT COLUMN_NAME,NULLABLE,DATA_DEFAULT FROM USER_TAB_COLUMNS'
    . ' WHERE TABLE_NAME=? AND COLUMN_NAME IN (?,?,?,?)',
    ['YDY_ARCHIVE', 'ORDER_NO', 'STATUS', 'IS_SHOW', 'LINK']) as $column) {
    $archiveDefaults[(string)$column['column_name']] = [
        'nullable' => (string)$column['nullable'],
        'has_default' => $column['data_default'] !== null,
    ];
}
$assert(($archiveDefaults['LINK']['nullable'] ?? '') === 'N', 'Archive link field unexpectedly nullable');
if ((string)getenv('POC_PREFLIGHT_ONLY') === '1') {
    echo json_encode(['suite' => 'station-archive-c', 'mode' => 'read-only-preflight',
        'isolated' => true, 'tables' => $metadata, 'archive_defaults' => $archiveDefaults,
        'station_marker_rows' => $count('SELECT COUNT(*) N FROM ' . $table('station')
            . ' WHERE NAME LIKE ?', ['CDXPARSTAT%']),
        'archive_marker_rows' => $count('SELECT COUNT(*) N FROM ' . $table('archive')
            . ' WHERE NAME LIKE ?', ['CDXPARARCH%'])], JSON_UNESCAPED_SLASHES) . PHP_EOL;
    exit(0);
}

require_once $root . '/application/base/Security.php';
$suffix = strtoupper(bin2hex(random_bytes(4)));
$operator = 'CDXPARSA' . $suffix;
$stationName = 'CDXPARSTAT' . $suffix . 'A';
$archiveName = 'CDXPARARCH' . $suffix . 'A';
$operatorId = 0;
$stationId = 0;
$archiveId = 0;
$stationUuid = '';
$archiveUuid = '';
$results = [];
$cleanup = [];
$failure = '';
try {
    $assert(strlen($stationName) <= 20 && strlen($archiveName) <= 20,
        'Synthetic names exceed validator limits');
    $assert($count('SELECT COUNT(*) N FROM ' . $table('user')
        . ' WHERE UUID=? OR ACCOUNT=?', [$operator, $operator]) === 0
        && $count('SELECT COUNT(*) N FROM ' . $table('station')
            . ' WHERE NAME=?', [$stationName]) === 0
        && $count('SELECT COUNT(*) N FROM ' . $table('archive')
            . ' WHERE NAME=?', [$archiveName]) === 0,
        'Synthetic marker already exists');
    $results['marker_before_zero'] = true;

    $unauthShow = $post('/p/station/show', []);
    $unauthLink = $post('/p/station/addArchive', ['uuid' => 'INVALID', 'archive_uuid' => 'INVALID']);
    $assert($pass($unauthShow, 100) && $pass($unauthLink, 100),
        'Station show/addArchive unauthenticated gate failed');
    $results['unauth_station_routes_rejected'] = true;

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

    $addArchiveType = $post('/p/archive/add', ['name' => $archiveName,
        'order_no' => '9999', 'link' => '/codex-test/' . $archiveName,
        'demo' => $archiveName], $token);
    $assert($pass($addArchiveType, 0, '添加成功'), 'Synthetic archive add HTTP contract failed');
    $archiveRows = $rows('SELECT ID,UUID,NAME,STATUS,LINK,IS_SHOW,CREATE_BY'
        . ' FROM ' . $table('archive') . ' WHERE NAME=? AND CREATE_BY=?',
        [$archiveName, $operator]);
    $assert(count($archiveRows) === 1, 'Synthetic archive row missing/ambiguous');
    $archiveId = (int)$archiveRows[0]['id'];
    $archiveUuid = trim((string)$archiveRows[0]['uuid']);
    $assert($archiveId > 0 && $archiveUuid !== ''
        && (int)$archiveRows[0]['status'] === 1
        && (int)$archiveRows[0]['is_show'] === 1
        && (string)$archiveRows[0]['link'] === '/codex-test/' . $archiveName,
        'Synthetic archive values mismatch');
    $results['archive_add_source_readback'] = true;

    $addStation = $post('/p/station/add', ['name' => $stationName,
        'demo' => $stationName], $token);
    $assert($pass($addStation, 0, '添加成功'), 'Synthetic station add HTTP contract failed');
    $stationRows = $rows('SELECT ID,UUID,NAME,STATUS,CREATE_BY FROM ' . $table('station')
        . ' WHERE NAME=? AND CREATE_BY=?', [$stationName, $operator]);
    $assert(count($stationRows) === 1, 'Synthetic station row missing/ambiguous');
    $stationId = (int)$stationRows[0]['id'];
    $stationUuid = trim((string)$stationRows[0]['uuid']);
    $assert($stationId > 0 && $stationUuid !== '' && (int)$stationRows[0]['status'] === 1,
        'Synthetic station values mismatch');
    $assert($count('SELECT COUNT(*) N FROM ' . $table('staff')
        . ' WHERE STATION_UUID=?', [$stationUuid]) === 0
        && $count('SELECT COUNT(*) N FROM ' . $table('station_archive')
            . ' WHERE STATION_UUID=? OR ARCHIVE_UUID=?', [$stationUuid, $archiveUuid]) === 0,
        'Synthetic station/archive already referenced');
    $results['station_add_zero_links_and_staff'] = true;

    $show = $post('/p/station/show', [], $token);
    $shown = $show['json']['data'] ?? null;
    $assert($pass($show, 0, '查询成功') && is_array($shown),
        'Station show HTTP contract failed');
    $seen = 0;
    foreach ($shown as $item) {
        if (is_array($item) && trim((string)($item['uuid'] ?? '')) === $stationUuid
            && (string)($item['name'] ?? '') === $stationName) ++$seen;
    }
    $assert($seen === 1, 'Synthetic station absent/duplicate in show');
    $results['station_show_contains_only_own_asserted_row'] = true;

    $link = $post('/p/station/addArchive', ['uuid' => $stationUuid,
        'archive_uuid' => $archiveUuid], $token);
    $linkRows = $rows('SELECT ID,STATION_UUID,ARCHIVE_UUID,CREATE_BY FROM '
        . $table('station_archive') . ' WHERE STATION_UUID=? OR ARCHIVE_UUID=?',
        [$stationUuid, $archiveUuid]);
    $assert($pass($link, 0, '添加成功') && count($linkRows) === 1
        && trim((string)$linkRows[0]['station_uuid']) === $stationUuid
        && trim((string)$linkRows[0]['archive_uuid']) === $archiveUuid
        && (string)$linkRows[0]['create_by'] === $operator,
        'Station addArchive HTTP code/message or exact relation failed');
    $results['station_add_archive_controller_contract_and_one_relation'] = true;

    $assert($count('SELECT COUNT(*) N FROM ' . $table('staff')
        . ' WHERE STATION_UUID=?', [$stationUuid]) === 0,
        'Synthetic station unexpectedly acquired staff');
    $deleteArchive = $post('/p/archive/delete', ['uuid' => $archiveUuid], $token);
    $afterDelete = $one('SELECT IS_SHOW FROM ' . $table('archive')
        . ' WHERE ID=? AND UUID=?', [$archiveId, $archiveUuid]);
    $assert($pass($deleteArchive, 0, '删除成功')
        && (int)($afterDelete['is_show'] ?? -1) === 0
        && $count('SELECT COUNT(*) N FROM ' . $table('station_archive')
            . ' WHERE STATION_UUID=? OR ARCHIVE_UUID=?', [$stationUuid, $archiveUuid]) === 0,
        'Archive delete did not soft-delete and unlink only own relation');
    $results['archive_delete_soft_and_relation_zero'] = true;
} catch (Throwable $e) {
    $failure = get_class($e) . ':' . hash('sha256', $e->getMessage());
} finally {
    try {
        $stationOwned = $rows('SELECT ID,UUID,NAME,CREATE_BY FROM ' . $table('station')
            . ' WHERE NAME=? AND CREATE_BY=?', [$stationName, $operator]);
        $archiveOwned = $rows('SELECT ID,UUID,NAME,CREATE_BY FROM ' . $table('archive')
            . ' WHERE NAME=? AND CREATE_BY=?', [$archiveName, $operator]);
        if (count($stationOwned) > 1 || count($archiveOwned) > 1) {
            throw new RuntimeException('Ambiguous synthetic relation cleanup');
        }
        if (count($stationOwned) === 1) {
            $item = $stationOwned[0];
            if ($stationId > 0 && ((int)$item['id'] !== $stationId
                || trim((string)$item['uuid']) !== $stationUuid)) {
                throw new RuntimeException('Synthetic station identity changed');
            }
            $stationUuid = trim((string)$item['uuid']);
            $stationId = (int)$item['id'];
        }
        if (count($archiveOwned) === 1) {
            $item = $archiveOwned[0];
            if ($archiveId > 0 && ((int)$item['id'] !== $archiveId
                || trim((string)$item['uuid']) !== $archiveUuid)) {
                throw new RuntimeException('Synthetic archive identity changed');
            }
            $archiveUuid = trim((string)$item['uuid']);
            $archiveId = (int)$item['id'];
        }
        if ($stationUuid !== '' && $count('SELECT COUNT(*) N FROM ' . $table('staff')
            . ' WHERE STATION_UUID=?', [$stationUuid]) !== 0) {
            throw new RuntimeException('Station acquired staff; refusing cleanup');
        }
        if ($stationUuid !== '' && $archiveUuid !== '') {
            $links = $rows('SELECT ID,STATION_UUID,ARCHIVE_UUID FROM ' . $table('station_archive')
                . ' WHERE STATION_UUID=? OR ARCHIVE_UUID=?', [$stationUuid, $archiveUuid]);
            foreach ($links as $item) {
                if (trim((string)$item['station_uuid']) !== $stationUuid
                    || trim((string)$item['archive_uuid']) !== $archiveUuid) {
                    throw new RuntimeException('Unexpected cross-fixture relation; refusing cleanup');
                }
            }
            foreach ($links as $item) {
                $statement = $db->prepare('DELETE FROM ' . $table('station_archive')
                    . ' WHERE ID=? AND STATION_UUID=? AND ARCHIVE_UUID=?');
                $statement->execute([(int)$item['id'], $stationUuid, $archiveUuid]);
            }
        }
        if (count($stationOwned) === 1) {
            $statement = $db->prepare('DELETE FROM ' . $table('station')
                . ' WHERE ID=? AND UUID=? AND NAME=? AND CREATE_BY=?');
            $statement->execute([$stationId, $stationUuid, $stationName, $operator]);
        }
        if (count($archiveOwned) === 1) {
            $statement = $db->prepare('DELETE FROM ' . $table('archive')
                . ' WHERE ID=? AND UUID=? AND NAME=? AND CREATE_BY=?');
            $statement->execute([$archiveId, $archiveUuid, $archiveName, $operator]);
        }
        if ($operatorId > 0) {
            $statement = $db->prepare('DELETE FROM ' . $table('user')
                . ' WHERE ID=? AND UUID=? AND ACCOUNT=?');
            $statement->execute([$operatorId, $operator, $operator]);
        }
        $cleanup = [
            'operator_zero' => $count('SELECT COUNT(*) N FROM ' . $table('user')
                . ' WHERE UUID=? OR ACCOUNT=?', [$operator, $operator]) === 0,
            'station_zero' => $count('SELECT COUNT(*) N FROM ' . $table('station')
                . ' WHERE NAME=?', [$stationName]) === 0,
            'archive_zero' => $count('SELECT COUNT(*) N FROM ' . $table('archive')
                . ' WHERE NAME=?', [$archiveName]) === 0,
            'relation_zero' => ($stationUuid === '' && $archiveUuid === '')
                || ($stationUuid !== '' && $archiveUuid !== ''
                    && $count('SELECT COUNT(*) N FROM ' . $table('station_archive')
                        . ' WHERE STATION_UUID=? OR ARCHIVE_UUID=?', [$stationUuid, $archiveUuid]) === 0),
            'staff_zero' => $stationUuid === '' || $count('SELECT COUNT(*) N FROM ' . $table('staff')
                . ' WHERE STATION_UUID=?', [$stationUuid]) === 0,
        ];
    } catch (Throwable $e) {
        $cleanup['error_class'] = get_class($e);
        $cleanup['error_sha256'] = hash('sha256', $e->getMessage());
    }
}
$clean = ($cleanup['operator_zero'] ?? false) && ($cleanup['station_zero'] ?? false)
    && ($cleanup['archive_zero'] ?? false) && ($cleanup['relation_zero'] ?? false)
    && ($cleanup['staff_zero'] ?? false);
echo json_encode(['suite' => 'station-archive-c', 'dialect' => 'dm', 'isolated' => true,
    'marker_suffix' => $suffix, 'results' => $results, 'failure' => $failure,
    'cleanup' => $cleanup], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
if ($failure !== '' || !$clean) exit(1);
