<?php
declare(strict_types=1);

$root = (string)getenv('POC_APP_ROOT');
$base = rtrim((string)getenv('POC_BASE_URL'), '/');
$dialect = (string)getenv('POC_DIALECT');
if (!is_file($root . '/application/base/Security.php')
    || !preg_match('#^http://127\.0\.0\.1:\d+$#', $base)
    || !in_array($dialect, ['mysql', 'dm'], true)) exit(2);
$boundary = json_decode((string)file_get_contents($base . '/__poc_boundary'), true);
if (empty($boundary['external_stubs']) || empty($boundary['redis_isolated'])) {
    throw new RuntimeException('Isolated HTTP boundary missing');
}
$connect = static function () use ($dialect): PDO {
    $host = getenv('POC_HOST'); $port = getenv('POC_PORT'); $schema = getenv('POC_SCHEMA');
    return new PDO($dialect === 'dm' ? "dm:host=$host;port=$port;schema=$schema"
        : "mysql:host=$host;port=$port;dbname=$schema;charset=utf8mb4",
        getenv('POC_USER'), getenv('POC_PASSWORD'), [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER,
        ]);
};
$db = $connect();
$table = $dialect === 'dm' ? 'YDY_HEALTH_ANALYSIS_REPORT' : 'ydy_health_analysis_report';
if (getenv('POC_AUDIT_ONLY') === '1') {
    $count = (int)$db->query('SELECT COUNT(*) FROM ' . $table . " WHERE REPORT_NAME LIKE 'CDXHRCRUD%'")->fetchColumn();
    echo json_encode(['audit_only' => true, 'synthetic_reports' => $count]) . PHP_EOL;
    exit($count === 0 ? 0 : 1);
}
$marker = 'CDXHRCRUD' . strtoupper(bin2hex(random_bytes(5)));
require_once $root . '/application/base/Security.php';
$token = \app\base\Security::encrypt(implode("\t", ['0', $marker, $marker, '0', '', '', $marker]));
$call = static function (string $action, array $form, bool $auth = true) use ($base, $token): array {
    $context = stream_context_create(['http' => ['method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\n" . ($auth ? 'token: ' . $token . "\r\n" : ''),
        'content' => http_build_query($form), 'timeout' => 45, 'ignore_errors' => true]]);
    $body = file_get_contents($base . '/p/zhaikerHealth/' . $action, false, $context);
    if (!preg_match('#^HTTP/\S+\s+200(?:\s|$)#', $http_response_header[0] ?? '')) {
        throw new RuntimeException($action . ': unexpected transport status');
    }
    $data = is_string($body) ? json_decode($body, true) : null;
    if (!is_array($data)) throw new RuntimeException($action . ': non-JSON HTTP response');
    return $data;
};
$requireSuccess = static function (array $response, string $action): void {
    if (($response['code'] ?? null) !== 0) {
        throw new RuntimeException($action . ': ' . ($response['message'] ?? 'missing code'));
    }
};
$read = static function (int $id) use (&$db, $table): array {
    $q = $db->prepare('SELECT * FROM ' . $table . ' WHERE ID=?'); $q->execute([$id]);
    $row = $q->fetch(PDO::FETCH_ASSOC);
    if (!$row) throw new RuntimeException('Original report row is missing');
    foreach ($row as &$v) if (is_resource($v)) $v = stream_get_contents($v);
    return $row;
};
$results = []; $failure = null;
try {
    foreach (['reportSave', 'reportList', 'reportInfo', 'reportUpdate', 'reportDelete', 'reportBatchDelete'] as $action) {
        $anon = $call($action, [], false);
        if (($anon['code'] ?? null) !== 100) throw new RuntimeException($action . ': no-auth contract');
    }
    $results['no_auth_six'] = true;
    $filters = ['metric_codes' => ['weight'], 'enabled' => true, 'missing' => null];
    // Empty members intentionally prevents the external health-profile dispatcher.
    $result = ['members' => [], '说明' => str_repeat('中文兼容', 3000), 'amount' => '0.80'];
    $chart = ['values' => [0, 70.5], 'labels' => ['昨日', '今日']];
    $params = ['report_type' => 'group_analysis', 'report_name' => $marker . '兼容报告',
        'ai_user_id' => 0, 'filters_json' => json_encode($filters, JSON_UNESCAPED_UNICODE),
        'result_json' => json_encode($result, JSON_UNESCAPED_UNICODE),
        'chart_json' => json_encode($chart, JSON_UNESCAPED_UNICODE)];
    $save = $call('reportSave', $params); $requireSuccess($save, 'reportSave');
    $id = (int)($save['data']['id'] ?? 0);
    if ($id <= 0) throw new RuntimeException('reportSave missing generated ID');
    $source = $read($id);
    if ($source['report_name'] !== $params['report_name']
        || json_decode($source['result_json'], true) !== $result
        || json_decode($source['filters_json'], true) !== $filters
        || json_decode($source['chart_json'], true) !== $chart) {
        throw new RuntimeException('reportSave source JSON/Chinese mismatch');
    }
    $results['save_original_json_bytes'] = strlen($source['result_json']);
    $info = $call('reportInfo', ['id' => $id]); $requireSuccess($info, 'reportInfo');
    if (($info['data']['result'] ?? null) !== $result || ($info['data']['filters'] ?? null) !== $filters
        || ($info['data']['chart'] ?? null) !== $chart) throw new RuntimeException('reportInfo decoded JSON mismatch');
    $results['info_json_parity'] = true;
    $results['info_time_seconds'] = preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', (string)$info['data']['create_time']) === 1;
    $list = $call('reportList', ['keyword' => $marker, 'page' => 1, 'page_size' => 1]);
    $requireSuccess($list, 'reportList');
    if (($list['data']['total'] ?? null) !== 1 || (int)($list['data']['rows'][0]['id'] ?? 0) !== $id) {
        throw new RuntimeException('reportList filtered row mismatch');
    }
    $results['list_time_seconds'] = preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', (string)$list['data']['rows'][0]['create_time']) === 1;
    $requireSuccess($call('reportUpdate', ['id' => $id, 'report_name' => $marker . '更新',
        'result_json' => '{"members":[],"value":0,"nullable":null}']), 'reportUpdate');
    $source = $read($id);
    $updated = $call('reportInfo', ['id' => $id]); $requireSuccess($updated, 'updated info');
    if ($source['report_name'] !== $marker . '更新'
        || json_decode($source['result_json'], true) !== ['members' => [], 'value' => 0, 'nullable' => null]
        || $updated['data']['result'] !== json_decode($source['result_json'], true)) {
        throw new RuntimeException('Updated source/API mismatch');
    }
    $results['update_readback'] = true;
    $requireSuccess($call('reportDelete', ['id' => $id]), 'reportDelete');
    if ((int)$read($id)['is_delete'] !== 1 || ($call('reportInfo', ['id' => $id])['code'] ?? 0) === 0) {
        throw new RuntimeException('Deleted report still readable');
    }
    $results['delete_soft_readback'] = true;
    $ids = [];
    foreach ([1, 2] as $n) {
        $saved = $call('reportSave', array_replace($params, ['report_name' => $marker . '批量' . $n]));
        $requireSuccess($saved, 'batch fixture'); $ids[] = (int)$saved['data']['id'];
    }
    $requireSuccess($call('reportBatchDelete', ['ids' => implode(',', $ids)]), 'reportBatchDelete');
    foreach ($ids as $batchId) if ((int)$read($batchId)['is_delete'] !== 1) throw new RuntimeException('Batch delete not persisted');
    $list = $call('reportList', ['keyword' => $marker]); $requireSuccess($list, 'deleted list');
    if (($list['data']['total'] ?? null) !== 0) throw new RuntimeException('Deleted reports in list');
    $results['batch_delete_readback'] = true;
    foreach ([['reportSave', array_replace($params, ['report_name' => ''])],
        ['reportSave', array_replace($params, ['report_type' => 'invalid'])], ['reportInfo', ['id' => 0]]] as [$action, $input]) {
        if (($call($action, $input)['code'] ?? 0) === 0) throw new RuntimeException('Invalid input accepted: ' . $action);
    }
    $results['invalid_inputs'] = true;
} catch (Throwable $e) {
    $failure = $e->getMessage();
} finally {
    // Reconnect so an uncertain network error cannot skip cleanup of this exact marker.
    $db = $connect();
    $q = $db->prepare('DELETE FROM ' . $table . ' WHERE REPORT_NAME LIKE ?'); $q->execute([$marker . '%']);
    $q = $db->prepare('SELECT COUNT(*) FROM ' . $table . ' WHERE REPORT_NAME LIKE ?'); $q->execute([$marker . '%']);
    if ((int)$q->fetchColumn() !== 0) throw new RuntimeException('Report cleanup left rows');
}
echo json_encode(['dialect' => $dialect, 'results' => $results, 'error' => $failure,
    'cleanup_zero' => true], JSON_UNESCAPED_UNICODE) . PHP_EOL;
exit($failure !== null || in_array(false, $results, true) ? 1 : 0);
