<?php

declare(strict_types=1);

// Isolated Store A/B/C only: no conversation/feedback fixture or business data read.
$base = rtrim((string) getenv('POC_BASE_URL'), '/');
$root = (string) getenv('POC_APP_ROOT');
$dialect = (string) getenv('POC_DIALECT');
if ($base === '' || !in_array($dialect, ['mysql', 'dm'], true)
    || strpos($root, '/tmp/zhct-') !== 0
    || !is_file($root . '/application/base/Security.php')
    || !is_file($root . '/env.php')) {
    throw new RuntimeException('Isolated Store runtime required');
}
require_once $root . '/env.php';
$boundary = json_decode((string) file_get_contents($base . '/__poc_boundary'), true);
if (!is_array($boundary) || empty($boundary['external_stubs'])
    || empty($boundary['redis_isolated'])) {
    throw new RuntimeException('POC isolation boundary unavailable');
}
$env = [];
foreach (['POC_HOST', 'POC_PORT', 'POC_SCHEMA', 'POC_USER', 'POC_PASSWORD'] as $name) {
    $env[$name] = (string) getenv($name);
}
if ($dialect === 'dm' && $env['POC_HOST'] === '') {
    $pid = (string) getenv('POC_RUNTIME_PID');
    if (!ctype_digit($pid)) {
        throw new RuntimeException('Approved DM runtime PID required');
    }
    foreach (explode("\0", (string) file_get_contents('/proc/' . $pid . '/environ')) as $entry) {
        $pair = explode('=', $entry, 2);
        if (count($pair) === 2 && array_key_exists($pair[0], $env)) {
            $env[$pair[0]] = $pair[1];
        }
    }
}
if ($dialect === 'mysql' && $env['POC_HOST'] === '') {
    $local = $root . '/application/config/local/database.php';
    $config = require (is_file($local) ? $local : $root . '/application/database.php');
    $env = [
        'POC_HOST' => (string) $config['hostname'],
        'POC_PORT' => (string) ($config['hostport'] ?? '3306'),
        'POC_SCHEMA' => (string) $config['database'],
        'POC_USER' => (string) $config['username'],
        'POC_PASSWORD' => (string) $config['password'],
    ];
}
foreach ($env as $value) {
    if ($value === '') {
        throw new RuntimeException('Approved DB runtime incomplete');
    }
}
$dsn = $dialect === 'dm'
    ? "dm:host={$env['POC_HOST']};port={$env['POC_PORT']};schema={$env['POC_SCHEMA']}"
    : "mysql:host={$env['POC_HOST']};port={$env['POC_PORT']};dbname={$env['POC_SCHEMA']};charset=utf8mb4";
$openDb = static function () use ($dsn, $env): PDO {
    return new PDO($dsn, $env['POC_USER'], $env['POC_PASSWORD'], [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]);
};
$prefix = $dialect === 'dm' ? 'YDY_' : 'ydy_';
$userTable = $prefix . ($dialect === 'dm' ? 'USER' : 'user');
$logTable = $prefix . ($dialect === 'dm' ? 'AGENT_CONVERSATION_LOG' : 'agent_conversation_log');
$feedbackTable = $prefix . ($dialect === 'dm' ? 'AGENT_FEEDBACK_LOG' : 'agent_feedback_log');
$marker = 'CDXPARAGENT' . strtoupper(bin2hex(random_bytes(6)));
$bytes = random_bytes(16);
$bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40);
$bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80);
$hex = strtoupper(bin2hex($bytes));
$userUuid = substr($hex, 0, 8) . '-' . substr($hex, 8, 4) . '-'
    . substr($hex, 12, 4) . '-' . substr($hex, 16, 4) . '-'
    . substr($hex, 20, 12);
$absentLogId = random_int(1000000000, 2147000000);
$userId = 0;
$token = '';
$phase = 'preflight';
$result = ['dialect' => $dialect, 'marker' => $marker, 'events' => [], 'cleanup' => []];

$count = static function (PDO $pdo, string $sql, array $bind = []): int {
    $statement = $pdo->prepare($sql);
    $statement->execute($bind);
    return (int) $statement->fetchColumn();
};
$counts = static function () use ($openDb, $count, $userTable,
    $logTable, $feedbackTable, $userUuid, $marker): array {
    $pdo = $openDb();
    return [
        'feedback_total' => $count($pdo, 'SELECT COUNT(*) FROM ' . $feedbackTable),
        'synthetic_feedback' => $count($pdo, 'SELECT COUNT(*) FROM ' . $feedbackTable
            . ' WHERE USER_UUID=?', [$userUuid]),
        'synthetic_log' => $count($pdo, 'SELECT COUNT(*) FROM ' . $logTable
            . ' WHERE QUESTION LIKE ?', [$marker . '%']),
        'synthetic_user' => $count($pdo, 'SELECT COUNT(*) FROM ' . $userTable
            . ' WHERE UUID=? OR ACCOUNT=?', [$userUuid, $marker]),
    ];
};
$shape = static function ($value) use (&$shape): string {
    if (!is_array($value)) {
        return gettype($value);
    }
    if ($value === []) {
        return '[]';
    }
    $keys = array_keys($value);
    if ($keys === range(0, count($keys) - 1)) {
        return '[' . implode(',', array_map($shape, $value)) . ']';
    }
    sort($keys, SORT_STRING);
    $parts = [];
    foreach ($keys as $key) {
        $parts[] = $key . ':' . $shape($value[$key]);
    }
    return '{' . implode(',', $parts) . '}';
};
$request = static function (string $method, string $path, array $form, bool $auth)
    use ($base, &$token, $shape): array {
    $headers = "Content-Type: application/x-www-form-urlencoded\r\n";
    if ($auth) {
        $headers .= 'token: ' . $token . "\r\n";
    }
    $context = stream_context_create(['http' => [
        'method' => $method,
        'header' => $headers,
        'content' => $method === 'POST'
            ? http_build_query($form, '', '&', PHP_QUERY_RFC3986) : '',
        'timeout' => 20,
        'ignore_errors' => true,
    ]]);
    $body = file_get_contents($base . $path, false, $context);
    $responseHeaders = $http_response_header ?? [];
    $statusLine = (string) ($responseHeaders[0] ?? '');
    $status = preg_match('/\s([0-9]{3})\s/', $statusLine, $match)
        ? (int) $match[1] : 0;
    $json = is_string($body) ? json_decode($body, true) : null;
    if ($status === 0 || !is_array($json)) {
        throw new RuntimeException('Negative endpoint response not HTTP JSON');
    }
    return ['http_status' => $status,
        'code' => $json['code'] ?? null,
        'message' => $json['message'] ?? $json['msg'] ?? null,
        'shape' => $shape($json)];
};

try {
    $before = $counts();
    $result['before'] = $before;
    if ($before['synthetic_feedback'] !== 0 || $before['synthetic_log'] !== 0
        || $before['synthetic_user'] !== 0
        || $count($openDb(), 'SELECT COUNT(*) FROM ' . $logTable . ' WHERE ID=?',
            [$absentLogId]) !== 0) {
        throw new RuntimeException('Synthetic negative marker/id collision');
    }

    foreach (['GET', 'POST'] as $method) {
        $phase = 'anonymous_' . strtolower($method) . '_history';
        $response = $request($method,
            '/p/agent/history?keyword=' . rawurlencode($marker)
            . '&page=1&page_size=10', [], false);
        $result['events'][] = ['step' => $phase] + $response;
        if (!is_int($response['code']) || $response['code'] === 0
            || $counts() !== $before) {
            throw new RuntimeException('Anonymous history not rejected or DB changed');
        }
    }
    $phase = 'anonymous_feedback';
    $response = $request('POST', '/p/agent/feedback',
        ['log_id' => 0, 'feedback' => 'helpful'], false);
    $result['events'][] = ['step' => $phase] + $response;
    if (!is_int($response['code']) || $response['code'] === 0
        || $counts() !== $before) {
        throw new RuntimeException('Anonymous feedback not rejected or DB changed');
    }

    $phase = 'synthetic_ordinary_operator';
    $db = $openDb();
    $insert = $db->prepare('INSERT INTO ' . $userTable
        . ' (UUID,ACCOUNT,PWD,SALT,NAME,TYPE,STATUS,CREATE_TIME,CREATE_BY,IS_DELETE)'
        . ' VALUES (?,?,?,?,?,?,?,?,?,?)');
    $insert->execute([$userUuid, $marker, 'DISABLED', strtoupper(bin2hex(random_bytes(8))),
        $marker, 1, 1, date('Y-m-d H:i:s'), 'CODEX', 0]);
    try { $userId = (int) $db->lastInsertId(); } catch (Throwable $ignored) {}
    $read = $openDb()->prepare('SELECT ID,TYPE FROM ' . $userTable
        . ' WHERE UUID=? AND ACCOUNT=?');
    $read->execute([$userUuid, $marker]);
    $rows = $read->fetchAll(PDO::FETCH_ASSOC);
    if (count($rows) !== 1 || (int) ($rows[0]['TYPE'] ?? $rows[0]['type']) !== 1) {
        throw new RuntimeException('Ordinary synthetic user mismatch');
    }
    $userId = (int) ($rows[0]['ID'] ?? $rows[0]['id']);
    require_once $root . '/application/base/Security.php';
    $token = \app\base\Security::encrypt(implode("\t", [
        (string) $userId, $userUuid, $marker, '1', '', '', $marker,
    ]));
    if (!is_string($token) || $token === '') {
        throw new RuntimeException('Synthetic ordinary token unavailable');
    }
    $expected = $before;
    $expected['synthetic_user'] = 1;
    if ($counts() !== $expected) {
        throw new RuntimeException('Synthetic operator inserted unexpected rows');
    }

    $phase = 'invalid_zero_log_id';
    $response = $request('POST', '/p/agent/feedback',
        ['log_id' => 0, 'feedback' => 'helpful'], true);
    $result['events'][] = ['step' => $phase] + $response;
    if ($response['code'] !== 1 || $counts() !== $expected) {
        throw new RuntimeException('Zero log ID not rejected or DB changed');
    }

    $phase = 'invalid_feedback_value';
    $response = $request('POST', '/p/agent/feedback',
        ['log_id' => $absentLogId, 'feedback' => 'CDXPARAGENT_INVALID'], true);
    $result['events'][] = ['step' => $phase] + $response;
    if ($response['code'] !== 1 || $counts() !== $expected) {
        throw new RuntimeException('Invalid feedback not rejected or DB changed');
    }
} catch (Throwable $error) {
    $result['failure'] = ['phase' => $phase,
        'class' => get_class($error), 'code' => $error->getCode()];
} finally {
    try {
        $cleanup = $openDb();
        $read = $cleanup->prepare('SELECT ID,UUID,ACCOUNT,TYPE FROM ' . $userTable
            . ' WHERE UUID=? AND ACCOUNT=?');
        $read->execute([$userUuid, $marker]);
        $rows = $read->fetchAll(PDO::FETCH_ASSOC);
        if (count($rows) > 1) {
            throw new RuntimeException('Ordinary user cleanup cardinality unsafe');
        }
        if ($rows) {
            $row = array_change_key_case($rows[0], CASE_LOWER);
            if ((int) $row['type'] !== 1 || ($userId > 0 && (int)$row['id'] !== $userId)) {
                throw new RuntimeException('Ordinary user cleanup identity mismatch');
            }
            $delete = $cleanup->prepare('DELETE FROM ' . $userTable
                . ' WHERE ID=? AND UUID=? AND ACCOUNT=? AND TYPE=1');
            $delete->execute([(int)$row['id'], $userUuid, $marker]);
            $result['cleanup']['user_deleted'] = $delete->rowCount();
        } else {
            $result['cleanup']['user_deleted'] = 0;
        }
        $result['cleanup']['after'] = $counts();
    } catch (Throwable $error) {
        $result['cleanup_error'] = ['class' => get_class($error),
            'code' => $error->getCode()];
    }
}
echo json_encode($result, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . PHP_EOL;
if (isset($result['failure']) || isset($result['cleanup_error'])
    || ($result['cleanup']['after'] ?? null) !== ($result['before'] ?? null)) {
    exit(1);
}
