<?php
// Disposable HTTP integration fixture for the zhctdev database. Run inside zhctapp_v2_0.
declare(strict_types=1);

$storeRoot = '/workspace/zhct/zhctproject/store';
require $storeRoot . '/env.php';
if (!defined('APP_PATH')) {
    define('APP_PATH', $storeRoot . '/application/');
}
$config = require $storeRoot . '/application/database.php';
if ($config['hostname'] !== '101.200.165.56' || $config['database'] !== 'zhct') {
    throw new RuntimeException('Unexpected database target; test aborted.');
}

$pdo = new PDO(
    'mysql:host=' . $config['hostname'] . ';port=' . ($config['hostport'] ?: 3306)
        . ';dbname=' . $config['database'] . ';charset=utf8mb4',
    $config['username'],
    $config['password'],
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);

function executeSql(PDO $pdo, string $sql, array $params): void
{
    $statement = $pdo->prepare($sql);
    $statement->execute($params);
}

function one(PDO $pdo, string $sql, array $params): array
{
    $statement = $pdo->prepare($sql);
    $statement->execute($params);
    return $statement->fetch() ?: [];
}

function requestApi(string $path, array $params): array
{
    $curl = curl_init('http://127.0.0.1' . $path);
    curl_setopt_array($curl, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query($params),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
    ]);
    $raw = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    $error = curl_error($curl);
    curl_close($curl);
    if ($raw === false || $status !== 200) {
        throw new RuntimeException('HTTP request failed: ' . $status . ' ' . $error);
    }
    $decoded = json_decode($raw, true);
    if (!is_array($decoded)) {
        throw new RuntimeException('Invalid JSON response.');
    }
    return $decoded;
}

function check(bool $condition, string $message): void
{
    if (!$condition) {
        throw new RuntimeException($message);
    }
}

$mode = one($pdo, 'SELECT config_value FROM ydy_config WHERE config_key=? AND status=1 LIMIT 1', ['face_learning_mode']);
check(($mode['config_value'] ?? null) === '2', 'face_learning_mode is not 2.');

$suffix = strtoupper(bin2hex(random_bytes(4)));
$deviceCode = 'CODEXFACE' . $suffix;
$staffUuid = sprintf('%s-%s-%s-%s-%s', substr($suffix, 0, 8), '1111', '4111', '8111', '111111111111');
$imageUuid = sprintf('%s-%s-%s-%s-%s', substr($suffix, 0, 8), '2222', '4222', '8222', '222222222222');
$hardwareUuid = sprintf('%s-%s-%s-%s-%s', substr($suffix, 0, 8), '3333', '4333', '8333', '333333333333');
$photoPath = 'static/upload/face/codex-' . strtolower($suffix) . '.jpg';
$feature = 'CODEX_TEST_FEATURE_' . $suffix;
$faceToken = 'CODEX_TEST_TOKEN_' . $suffix;
$now = date('Y-m-d H:i:s');
$ids = ['equipment' => null, 'staff' => null, 'staff_face' => null, 'hardware_img' => null];
$result = [];

try {
    executeSql($pdo, 'INSERT INTO ydy_equipment(code,name,type,status,mate_flag,face_time,create_time,create_by,update_time,update_by) VALUES(?,?,?,?,?,?,?,?,?,?)',
        [$deviceCode, 'Codex人脸接口测试', 2, 2, 1, '2001-01-01 00:00:00', $now, 'codex_test', $now, 'codex_test']);
    $ids['equipment'] = (int)$pdo->lastInsertId();

    executeSql($pdo, 'INSERT INTO ydy_staff(uuid,station_uuid,name,birthday,sport_uuid,sport_event_uuid,in_time,create_time,create_by,personnel_umber,permanent_address,coach_name,sport_year,is_show) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
        [$staffUuid, '', 'CODEX测试人员', '2000-01-01', '', '', '2026-10-10', $now, 'codex_test', 'CODEX-' . $suffix, '', '', '', 1]);
    $ids['staff'] = (int)$pdo->lastInsertId();

    executeSql($pdo, 'INSERT INTO ydy_hardware_img(uuid,hardware_uuid,staff_uuid,head_img,head_img_url,feature,face_token,group_id,baidu_user_id,baidu_face_token,is_del) VALUES(?,?,?,?,?,?,?,?,?,?,?)',
        [$imageUuid, $hardwareUuid, $staffUuid, '', $photoPath, '', '', 'codex_test', 'CODEX-' . $suffix, '', 0]);
    $ids['hardware_img'] = (int)$pdo->lastInsertId();

    executeSql($pdo, 'INSERT INTO ydy_staff_face(staff_uuid,hardware_img_uuid,hardware_uuid,head_img_url,feature,group_id,baidu_user_id,baidu_face_token,is_del) VALUES(?,?,?,?,?,?,?,?,?)',
        [$staffUuid, $imageUuid, $hardwareUuid, $photoPath, '', 'codex_test', 'CODEX-' . $suffix, '', 0]);
    $ids['staff_face'] = (int)$pdo->lastInsertId();

    $full = requestApi('/p/api/facedata', ['code' => $deviceCode, 'type' => 1, 'staff_uuid' => $staffUuid]);
    check(($full['code'] ?? null) === 0, 'Full sync failed: ' . ($full['message'] ?? ''));
    check(($full['face_learning_mode'] ?? null) === 2, 'Missing mode 2 in full sync.');
    check(!empty($full['trace_id']), 'Full sync trace_id missing.');
    check(count($full['data']['face_data'] ?? []) === 1, 'Expected exactly one fixture face.');
    $face = $full['data']['face_data'][0];
    check((int)$face['id'] === $ids['staff'], 'Staff ID mismatch.');
    check($face['face_token'] === '' && $face['feature'] === '', 'Unlearned face fields are not empty.');
    $result['full_before'] = ['code' => $full['code'], 'face_learning_mode' => $full['face_learning_mode'], 'count' => 1, 'trace_id' => $full['trace_id']];

    $params = ['code' => $deviceCode, 'staff_id' => $ids['staff'], 'head_img_url' => $face['head_img_url'],
        'feature' => $feature, 'baidu_face_token' => $faceToken];
    $stale = requestApi('/p/api/facedataLearnResult', array_merge($params, ['head_img_url' => $face['head_img_url'] . '.old']));
    check(($stale['code'] ?? null) === 1 && ($stale['message'] ?? null) === '人脸照片已更新，请重新拉取', 'Stale photo should fail.');
    check(!empty($stale['trace_id']), 'Stale photo trace_id missing.');
    $result['stale_photo'] = ['code' => $stale['code'], 'message' => $stale['message'], 'trace_id' => $stale['trace_id']];

    $saved = requestApi('/p/api/facedataLearnResult', $params);
    check(($saved['code'] ?? null) === 0 && !empty($saved['trace_id']), 'Learning result save failed: ' . ($saved['message'] ?? ''));
    $retry = requestApi('/p/api/facedataLearnResult', $params);
    check(($retry['code'] ?? null) === 0, 'Same-result retry should succeed.');
    $result['save'] = ['code' => $saved['code'], 'message' => $saved['message'], 'trace_id' => $saved['trace_id']];
    $result['retry'] = ['code' => $retry['code'], 'trace_id' => $retry['trace_id'] ?? null];

    $staffFace = one($pdo, 'SELECT feature,baidu_face_token FROM ydy_staff_face WHERE id=?', [$ids['staff_face']]);
    $hardwareFace = one($pdo, 'SELECT feature,baidu_face_token,face_token FROM ydy_hardware_img WHERE id=?', [$ids['hardware_img']]);
    check($staffFace['feature'] === $feature && $staffFace['baidu_face_token'] === $faceToken, 'staff_face was not updated.');
    check($hardwareFace['feature'] === $feature && $hardwareFace['baidu_face_token'] === $faceToken
        && $hardwareFace['face_token'] === $faceToken, 'hardware_img was not updated.');
    $result['database_readback'] = ['staff_face_updated' => true, 'hardware_img_updated' => true];

    $after = requestApi('/p/api/facedata', ['code' => $deviceCode, 'type' => 1, 'staff_uuid' => $staffUuid]);
    check(($after['code'] ?? null) === 0 && ($after['data']['face_data'][0]['face_token'] ?? null) === $faceToken, 'Full sync did not return learned token.');
    $result['full_after'] = ['code' => $after['code'], 'face_learning_mode' => $after['face_learning_mode'], 'token_present' => true, 'trace_id' => $after['trace_id'] ?? null];
} finally {
    foreach (['staff_face', 'hardware_img', 'staff', 'equipment'] as $table) {
        if ($ids[$table] !== null) {
            executeSql($pdo, 'DELETE FROM ydy_' . $table . ' WHERE id=?', [$ids[$table]]);
        }
    }
    $result['fixture_cleaned'] = true;
    echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), PHP_EOL;
}
