<?php
declare(strict_types=1);

// Exact recovery of the one B no-staff fixture after a test-harness boundary false negative.
$phase = (string)($argv[1] ?? '');
if (!in_array($phase, ['--preflight', '--cleanup', '--audit'], true)) exit(78);
if ($phase === '--cleanup' && (
    (string)getenv('CDX_PERSONAL_RECOVER_APPROVAL') !== 'RECOVER_B_USER_46900_V1'
    || !hash_equals((string)getenv('CDX_PERSONAL_RECOVER_SHA'),
        (string)hash_file('sha256', __FILE__)))) {
    fwrite(STDERR, "DENIED: exact owned-user cleanup approval required\n");
    exit(78);
}
const MARKER = 'CDXPERSONAL8A6F31D2';
const USER_ID = 46900;
const CREATE_TIME = 1791471007;
const UPDATE_TIME = 1791471009;
$journalDir = sys_get_temp_dir() . '/cdx-personal-no-staff-b-cdxpersonal8a6f31d2';
$journalPath = $journalDir . '/journal.json';
if (!is_dir($journalDir) || is_link($journalDir)
    || !is_file($journalPath) || is_link($journalPath)
    || ((fileperms($journalDir) & 0077) !== 0)
    || ((fileperms($journalPath) & 0077) !== 0)) {
    throw new RuntimeException('Exact private journal path/permissions drift');
}
$journal = json_decode((string)file_get_contents($journalPath), true);
if (!is_array($journal) || ($journal['lane'] ?? '') !== 'B'
    || ($journal['marker'] ?? '') !== MARKER
    || (int)($journal['id'] ?? 0) !== USER_ID
    || !preg_match('/^CDXPERSONALT[0-9A-F]{32}$/', (string)($journal['token'] ?? ''))
    || !in_array(($journal['phase'] ?? ''), ['http_completed', 'cleanup_committed'], true)) {
    throw new RuntimeException('Journal owner/state drift');
}
$cfg = parse_ini_file('/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local',
    true, INI_SCANNER_RAW);
if (!is_array($cfg) || !isset($cfg['DATABASE'], $cfg['DATABASE_ZHCT'])
    || strtolower((string)$cfg['DATABASE']['TYPE']) !== 'mysql'
    || (string)$cfg['DATABASE']['HOSTPORT'] !== '3306'
    || (string)$cfg['DATABASE']['CHARSET'] !== 'utf8') {
    throw new RuntimeException('B code-local MySQL identity drift');
}
$connect = static function (array $d): PDO {
    return new PDO('mysql:host=' . $d['HOSTNAME'] . ';port=' . $d['HOSTPORT']
        . ';dbname=' . $d['DATABASE'] . ';charset=' . $d['CHARSET'],
        $d['USERNAME'], $d['PASSWORD'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
};
$ai = $connect($cfg['DATABASE']);
$store = $connect($cfg['DATABASE_ZHCT']);
$count = static function (PDO $db, string $sql, array $args = []): int {
    $s = $db->prepare($sql);
    $s->execute($args);
    return (int)$s->fetchColumn();
};
$read = static function (PDO $db, string $sql, array $args = []): array {
    $s = $db->prepare($sql);
    $s->execute($args);
    $row = $s->fetch(PDO::FETCH_ASSOC);
    return is_array($row) ? $row : [];
};
$expectedName = MARKER . '-????';
$user = $read($ai, 'SELECT USER_ID,STORE_ID,MOBILE,NICK_NAME,GENDER,IS_DELETE,'
    . 'CREATE_TIME,UPDATE_TIME FROM yoshop_user WHERE USER_ID=?', [USER_ID]);
$userPresent = $user !== [];
if ($phase !== '--audit') {
    if ((int)($user['user_id'] ?? 0) !== USER_ID
        || (int)($user['store_id'] ?? 0) !== 10001
        || (string)($user['mobile'] ?? 'X') !== ''
        || (string)($user['nick_name'] ?? '') !== $expectedName
        || (int)($user['gender'] ?? 0) !== 1
        || (int)($user['is_delete'] ?? 1) !== 0
        || (int)($user['create_time'] ?? 0) !== CREATE_TIME
        || (int)($user['update_time'] ?? 0) !== UPDATE_TIME) {
        throw new RuntimeException('Owned user row changed; no automatic cleanup');
    }
}
$refs = $ai->query("SELECT TABLE_NAME,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS"
    . " WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME LIKE 'yoshop_%'"
    . " AND UPPER(COLUMN_NAME)='USER_ID' ORDER BY TABLE_NAME")->fetchAll(PDO::FETCH_ASSOC);
if (count($refs) !== 64) throw new RuntimeException('USER_ID reference inventory drift');
$externalRefs = 0;
foreach ($refs as $ref) {
    $table = (string)$ref['table_name'];
    if (!preg_match('/^yoshop_[a-z0-9_]+$/i', $table)) {
        throw new RuntimeException('Unsafe reference table metadata');
    }
    if (strcasecmp($table, 'yoshop_user') === 0) continue;
    $externalRefs += $count($ai, 'SELECT COUNT(*) FROM ' . $table
        . ' WHERE USER_ID=?', [USER_ID]);
}
$staff = $count($ai, 'SELECT COUNT(*) FROM yoshop_staff WHERE USER_ID=?', [USER_ID]);
$storeRefs = $count($store, 'SELECT COUNT(*) FROM ydy_staff WHERE AI_USER_ID=?', [USER_ID])
    + $count($store, 'SELECT COUNT(*) FROM ydy_user WHERE AI_USER_ID=?', [USER_ID]);
if ($staff !== 0 || $externalRefs !== 0 || $storeRefs !== 0) {
    throw new RuntimeException('External/staff reference exists; cleanup refused');
}
if ($phase === '--preflight') {
    echo json_encode(['phase' => $phase, 'owned_id' => USER_ID,
        'owned_row_exact' => true, 'stored_fourbyte_replaced_with_question_marks' => true,
        'staff_zero' => true, 'external_refs_zero' => true, 'store_refs_zero' => true,
        'journal_phase' => $journal['phase'], 'business_dml' => 0]), PHP_EOL;
    exit(0);
}
if ($phase === '--cleanup') {
    if ($journal['phase'] !== 'http_completed') {
        throw new RuntimeException('Cleanup state not exact');
    }
    $ai->beginTransaction();
    try {
        $delete = $ai->prepare('DELETE FROM yoshop_user WHERE USER_ID=? AND STORE_ID=10001'
            . ' AND MOBILE=? AND NICK_NAME=? AND GENDER=1 AND IS_DELETE=0'
            . ' AND CREATE_TIME=? AND UPDATE_TIME=?');
        $delete->execute([USER_ID, '', $expectedName, CREATE_TIME, UPDATE_TIME]);
        if ($delete->rowCount() !== 1) throw new RuntimeException('Exact delete did not affect one row');
        $ai->commit();
    } catch (Throwable $error) {
        if ($ai->inTransaction()) $ai->rollBack();
        throw $error;
    }
    $journal['phase'] = 'cleanup_committed';
    $body = json_encode($journal, JSON_UNESCAPED_SLASHES);
    $tmp = $journalPath . '.tmp';
    if (!is_string($body) || file_put_contents($tmp, $body, LOCK_EX) !== strlen($body)
        || !chmod($tmp, 0600) || !rename($tmp, $journalPath)) {
        throw new RuntimeException('Cleanup committed; private journal update failed');
    }
    echo json_encode(['phase' => $phase, 'deleted_exact_owned_user' => 1,
        'independent_audit_required' => true]), PHP_EOL;
    exit(0);
}
if ($journal['phase'] !== 'cleanup_committed' || $userPresent
    || $count($ai, 'SELECT COUNT(*) FROM yoshop_user WHERE NICK_NAME LIKE ?', [MARKER . '%']) !== 0) {
    throw new RuntimeException('Independent owned user zero failed');
}
putenv('POC_DIALECT=mysql');
foreach (['DATABASE' => 'AI_', 'DATABASE_ZHCT' => ''] as $section => $prefix) {
    foreach (['HOSTNAME' => 'HOST', 'HOSTPORT' => 'PORT', 'DATABASE' => 'SCHEMA',
        'USERNAME' => 'USER', 'PASSWORD' => 'PASSWORD'] as $from => $to) {
        putenv('POC_' . $prefix . $to . '=' . $cfg[$section][$from]);
    }
}
define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
$root = '/private/tmp/zhct-http-ai-macos.x2CCZX';
require $root . '/vendor/autoload.php';
(new \think\App($root))->setEnvName('poc')->initialize();
if (\think\facade\Cache::has($journal['token'])) {
    throw new RuntimeException('Independent token still live');
}
if (!unlink($journalPath) || !rmdir($journalDir)
    || file_exists($journalPath) || is_dir($journalDir)) {
    throw new RuntimeException('Verified journal removal failed');
}
echo json_encode(['phase' => $phase, 'owned_user_staff_refs_token_zero' => true,
    'journal_absent' => true, 'business_dml' => 0]), PHP_EOL;
