<?php
declare(strict_types=1);

// Exact B/C isolated, self-owned, single-parent write acceptance. No retries.
$phase = (string)($argv[1] ?? '');
$lane = (string)($argv[2] ?? '');
$marker = (string)($argv[3] ?? '');
$auditId = (int)($argv[4] ?? 0);
if (!in_array($phase, ['--probe', '--run', '--audit'], true)
    || !in_array($lane, ['B', 'C'], true)
    || !preg_match('/^CDXDLW[0-9A-F]{10}$/', $marker)
    || ($phase === '--run' && (string)getenv('CDX_DELIVERY_WRITE3_RUN')
        !== 'ONE_MARKER_ADD_EDIT_DELETE_V1')) {
    fwrite(STDERR, "DENIED: fixed phase/lane/marker/run gate required\n");
    exit(78);
}
$stage = 'startup';
set_exception_handler(static function (Throwable $e) use (&$stage, $marker): void {
    fwrite(STDERR, 'WRITE3_FATAL stage=' . $stage . ' marker=' . $marker
        . ' class=' . get_class($e) . "\n");
    exit(3);
});

$root = $lane === 'B'
    ? '/private/tmp/zhct-http-ai-macos.x2CCZX'
    : '/tmp/zhct-ai-http-20260929.3XDj3w';
$port = $lane === 'B' ? 18101 : 18102;
$pid = $lane === 'B' ? 13127 : 53772;
$base = 'http://127.0.0.1:' . $port;
if (!is_dir($root) || !is_file($root . '/public/index.php')
    || !is_file($root . '/vendor/autoload.php')) {
    throw new RuntimeException('Isolated app missing');
}
$builderActual = $lane === 'B'
    ? '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/ai_api/app/common/database/DmBuilder.php'
    : '/tmp/zhct-dm-poc-20260928.fcpiiN/ai_api/app/common/database/DmBuilder.php';
$modelActual = $lane === 'B'
    ? '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/ai_api/app/common/model/DeliveryRule.php'
    : '/tmp/zhct-dm-poc-20260928.fcpiiN/ai_api/app/common/model/DeliveryRule.php';
$builderSha = '9ece7684e98997d45183375b1d5baaca42e4aac788b1cf4d073a0ebf5752aa35';
if (hash_file('sha256', $root . '/app/common/database/DmBuilder.php') !== $builderSha
    || hash_file('sha256', $builderActual) !== $builderSha
    || hash_file('sha256', $root . '/app/common/model/DeliveryRule.php')
        !== '3413695d32e92adef05d3512db2060ff96da49519200f140e16286bdf7dbed09'
    || hash_file('sha256', $modelActual)
        !== '3413695d32e92adef05d3512db2060ff96da49519200f140e16286bdf7dbed09') {
    throw new RuntimeException('Isolated builder/model source drift');
}
if ($lane === 'B') {
    $stage = 'B-identity';
    $local = parse_ini_file(
        '/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local',
        true, INI_SCANNER_RAW);
    if (!is_array($local) || !isset($local['DATABASE'], $local['DATABASE_ZHCT'])) {
        throw new RuntimeException('Code-local development config absent');
    }
    $cfg = $local['DATABASE'];
    $ps = (string)shell_exec('/bin/ps eww -p ' . $pid . ' -o command=');
    $owners = trim((string)shell_exec('/usr/sbin/lsof -nP -t -iTCP:'
        . $port . ' -sTCP:LISTEN 2>/dev/null'));
    $has = static function (string $key, string $value) use ($ps): bool {
        return $value !== '' && strpos($ps, $key . '=' . $value) !== false;
    };
    foreach (['HOSTNAME' => 'HOST', 'HOSTPORT' => 'PORT',
        'DATABASE' => 'SCHEMA', 'USERNAME' => 'USER',
        'PASSWORD' => 'PASSWORD'] as $from => $to) {
        if (!$has('POC_AI_' . $to, (string)($cfg[$from] ?? ''))) {
            throw new RuntimeException('B worker/AI local DB mismatch');
        }
    }
    if (strtolower((string)($cfg['TYPE'] ?? '')) !== 'mysql'
        || (string)($cfg['HOSTPORT'] ?? '') !== '3306'
        || $owners !== (string)$pid
        || strpos($ps, 'POC_DIALECT=mysql') === false
        || strpos($ps, 'POC_EXTERNAL_STUBS=1') === false
        || hash_file('sha256', $root . '/config/database.php')
            !== '8c46a6d9cd277f2950818d09162f639a62f135a5eb6553e6c61ce2c19167fd02'
        || hash_file('sha256', $root . '/config/cache.php')
            !== '98fa13484feb3db94bc4ebdd3de2f0b4ebc2c80d1cccb7a283a16f45eb9095a4'
        || hash_file('sha256', $root . '/public/index.php')
            !== '87ff41c14c0380cfba8cb99b02c8e440ad90f975e2e129dfb6e5657a36a7868d') {
        throw new RuntimeException('B isolated runtime drift');
    }
    $connect = static function () use ($cfg): PDO {
        return new PDO('mysql:host=' . $cfg['HOSTNAME']
            . ';port=' . $cfg['HOSTPORT'] . ';dbname=' . $cfg['DATABASE']
            . ';charset=utf8', $cfg['USERNAME'], $cfg['PASSWORD'],
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_CASE => PDO::CASE_LOWER]);
    };
    $table = 'yoshop_delivery';
    $ruleTable = 'yoshop_delivery_rule';
    $goodsTable = 'yoshop_goods';
} else {
    $stage = 'C-identity';
    $env = [];
    foreach (explode("\0", (string)file_get_contents('/proc/' . $pid . '/environ'))
        as $item) {
        $pair = explode('=', $item, 2);
        if (count($pair) === 2) $env[$pair[0]] = $pair[1];
    }
    if (($env['POC_TARGET_ROOT'] ?? '') !== $root
        || ($env['POC_BASE_URL'] ?? '') !== $base
        || ($env['POC_DIALECT'] ?? '') !== 'dm'
        || ($env['POC_EXTERNAL_STUBS'] ?? '') !== '1'
        || ($env['POC_AI_HOST'] ?? '') !== '127.0.0.1'
        || ($env['POC_AI_PORT'] ?? '') !== '5238'
        || ($env['POC_AI_SCHEMA'] ?? '') !== 'AIZHCT_APP'
        || ($env['POC_AI_USER'] ?? '') !== 'AIZHCT_APP'
        || !in_array('dm', PDO::getAvailableDrivers(), true)) {
        throw new RuntimeException('C isolated runtime drift');
    }
    $connect = static function () use ($env): PDO {
        $d = new PDO('dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP',
            $env['POC_AI_USER'], $env['POC_AI_PASSWORD'],
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_CASE => PDO::CASE_LOWER]);
        if ($d->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")
            ->fetchColumn() !== 'AIZHCT_APP') {
            throw new RuntimeException('C effective schema mismatch');
        }
        return $d;
    };
    $table = 'YOSHOP_DELIVERY';
    $ruleTable = 'YOSHOP_DELIVERY_RULE';
    $goodsTable = 'YOSHOP_GOODS';
}
$stage = 'source-preflight';
$db = $connect();
$select = static function (PDO $d, string $sql, array $args = []): array {
    $s = $d->prepare($sql);
    $s->execute($args);
    return $s->fetchAll(PDO::FETCH_ASSOC);
};
$count = static function (PDO $d, string $sql, array $args = []): int {
    $s = $d->prepare($sql);
    $s->execute($args);
    return (int)$s->fetchColumn();
};
$owned = static function (PDO $d) use ($select, $table, $marker): array {
    return $select($d, 'SELECT * FROM ' . $table
        . ' WHERE STORE_ID=10001 AND NAME IN (?,?) ORDER BY DELIVERY_ID',
        [$marker, $marker . 'E']);
};
$natural = static function (PDO $d) use ($select, $table, $ruleTable): string {
    $parents = $select($d, 'SELECT * FROM ' . $table
        . " WHERE STORE_ID=10001 AND NAME NOT LIKE 'CDXDLW%' ORDER BY DELIVERY_ID");
    if (count($parents) > 50) throw new RuntimeException('Natural snapshot cap exceeded');
    $children = [];
    foreach ($parents as $row) {
        $id = (int)$row['delivery_id'];
        $children[(string)$id] = $select($d, 'SELECT * FROM ' . $ruleTable
            . ' WHERE DELIVERY_ID=? ORDER BY RULE_ID', [$id]);
    }
    return hash('sha256', json_encode([$parents, $children],
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
};
$originalNatural = $natural($db);
$markerRows = $owned($db);
if ($phase === '--probe' || $phase === '--audit') {
    $children = $auditId > 0 ? $count($db, 'SELECT COUNT(*) FROM '
        . $ruleTable . ' WHERE DELIVERY_ID=?', [$auditId]) : null;
    $goods = $auditId > 0 ? $count($db, 'SELECT COUNT(*) FROM '
        . $goodsTable . ' WHERE DELIVERY_ID=?', [$auditId]) : null;
    $ruleTotal = $count($db, 'SELECT COUNT(*) FROM ' . $ruleTable
        . ' WHERE STORE_ID=10001');
    if ($ruleTotal > 500) throw new RuntimeException('Rule orphan scan cap exceeded');
    $markerRules = 0;
    foreach ($select($db, 'SELECT REGION_TEXT FROM ' . $ruleTable
        . ' WHERE STORE_ID=10001') as $r) {
        if (strpos((string)$r['region_text'], $marker) !== false) $markerRules++;
    }
    echo json_encode(['lane' => $lane, 'phase' => $phase,
        'marker' => $marker, 'marker_parent_count' => count($markerRows),
        'audited_child_count' => $children, 'audited_goods_count' => $goods,
        'marker_rule_count' => $markerRules,
        'natural_sha256' => $originalNatural,
        'business_http' => 0, 'business_dml' => 0],
        JSON_UNESCAPED_SLASHES), "\n";
    exit(0);
}
if (count($markerRows) !== 0
    || $count($db, 'SELECT COUNT(*) FROM ' . $table
        . ' WHERE STORE_ID=10001 AND NAME LIKE ?', [$marker . '%']) !== 0
    || $count($db, 'SELECT COUNT(*) FROM ' . $goodsTable
        . ' WHERE DELIVERY_ID=?', [0]) < 0) {
    throw new RuntimeException('Marker not fresh or goods preflight failed');
}
if ($count($db, 'SELECT COUNT(*) FROM ' . ($lane === 'B'
        ? 'yoshop_region' : 'YOSHOP_REGION') . ' WHERE ID=1') !== 1) {
    throw new RuntimeException('Shared region reference absent');
}
$boundaryRaw = @file_get_contents($base . '/__poc_boundary', false,
    stream_context_create(['http' => ['timeout' => 5]]));
$boundary = is_string($boundaryRaw) ? json_decode($boundaryRaw, true) : null;
if ($lane === 'C' && (!is_array($boundary) || empty($boundary['external_stubs']))) {
    throw new RuntimeException('C external stub boundary absent');
}
if ($lane === 'B' && !is_string($boundaryRaw)) {
    throw new RuntimeException('B HTTP boundary absent');
}

define('TP_ENV', $lane === 'B' ? 'local' : 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require_once $root . '/vendor/autoload.php';
(new \think\App($root))->setEnvName($lane === 'B' ? 'local' : 'poc')->initialize();
if (config('cache.default') !== 'file') {
    throw new RuntimeException('Isolated File cache not effective');
}

$shape = null;
$shape = static function ($v) use (&$shape) {
    if (is_array($v)) {
        if (array_values($v) === $v) {
            return ['@array' => count($v), '@item' => count($v) ? $shape($v[0]) : null];
        }
        $r = [];
        foreach ($v as $k => $item) $r[(string)$k] = $shape($item);
        ksort($r);
        return $r;
    }
    if (is_int($v)) return 'integer';
    if (is_float($v)) return 'double';
    if (is_bool($v)) return 'boolean';
    if (is_string($v)) return 'string';
    return 'null';
};
$token = 'CDXDLWTOKEN' . strtoupper(bin2hex(random_bytes(16)));
$actor = ['user' => [
    'store_user_id' => 2147480002, 'store_id' => 10001,
    'user_name' => $marker, 'real_name' => $marker, 'is_super' => 1,
], 'store_id' => 10001, 'is_login' => true];
$http = static function (string $action, array $data) use ($base, $token, $shape): array {
    $url = $base . '/index.php?s=/store/setting.delivery/' . $action;
    $body = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if (!is_string($body)) throw new RuntimeException('POST JSON encode failed');
    $context = stream_context_create(['http' => [
        'method' => 'POST', 'timeout' => 20, 'ignore_errors' => true,
        'follow_location' => 0,
        'header' => "Content-Type: application/json;charset=utf-8\r\n"
            . 'Access-Token: ' . $token . "\r\n",
        'content' => $body,
    ]]);
    $started = microtime(true);
    $raw = @file_get_contents($url, false, $context);
    preg_match('/HTTP\/\S+\s+(\d+)/', $http_response_header[0] ?? '', $m);
    $decoded = is_string($raw) ? json_decode($raw, true) : null;
    unset($raw);
    return ['http' => isset($m[1]) ? (int)$m[1] : 0,
        'status' => is_array($decoded) ? ($decoded['status'] ?? null) : null,
        'json' => is_array($decoded),
        'json_type_shape' => is_array($decoded) ? $shape($decoded) : null,
        'elapsed_ms' => (int)round((microtime(true) - $started) * 1000)];
};
$snapshotOwned = static function (PDO $d, int $id) use ($select, $table, $ruleTable): array {
    $p = $select($d, 'SELECT * FROM ' . $table
        . ' WHERE DELIVERY_ID=? AND STORE_ID=10001', [$id]);
    $r = $select($d, 'SELECT * FROM ' . $ruleTable
        . ' WHERE DELIVERY_ID=? AND STORE_ID=10001 ORDER BY RULE_ID', [$id]);
    return ['parent' => $p, 'rules' => $r,
        'sha256' => hash('sha256', json_encode([$p, $r],
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES))];
};
$verifyState = static function (array $state, int $id, string $expectedName,
    int $delete, string $firstFee, string $additionalFee) use ($marker): array {
    $parent = $state['parent'];
    $rules = $state['rules'];
    if (count($parent) !== 1 || count($rules) !== 1) {
        throw new RuntimeException('Self-owned parent/rule count mismatch');
    }
    $p = $parent[0];
    $r = $rules[0];
    if ((int)$p['delivery_id'] !== $id
        || (string)$p['name'] !== $expectedName
        || (int)$p['store_id'] !== 10001
        || (int)$p['is_delete'] !== $delete
        || (int)$p['method'] !== 10
        || (int)$r['delivery_id'] !== $id
        || (int)$r['store_id'] !== 10001
        || bccomp((string)$r['first_fee'], $firstFee, 2) !== 0
        || bccomp((string)$r['additional_fee'], $additionalFee, 2) !== 0) {
        throw new RuntimeException('Self-owned row field readback mismatch');
    }
    $region = json_decode((string)$r['region'], true);
    $regionText = json_decode((string)$r['region_text'], true);
    if ($region !== [1] || !is_array($regionText)
        || count($regionText) !== 1
        || ($regionText[0]['name'] ?? null) !== $marker) {
        throw new RuntimeException('Self-owned CLOB/JSON rule readback mismatch');
    }
    return ['parent_count' => 1, 'rule_count' => 1,
        'parent_id' => $id, 'soft_deleted' => $delete === 1,
        'fees_exact' => true, 'region_json_exact' => true,
        'row_sha256' => $state['sha256']];
};
$form = static function (string $name, int $sort,
    string $firstFee, string $additionalFee) use ($marker): array {
    return ['name' => $name, 'method' => 10, 'sort' => $sort,
        'rules' => [[
            'region' => [1],
            'region_text' => [['name' => $marker, 'citys' => []]],
            'first' => 1, 'first_fee' => $firstFee,
            'additional' => 1, 'additional_fee' => $additionalFee,
        ]]];
};
$addForm = $form($marker, 90001, '5.50', '0.01');
$editForm = $form($marker . 'E', 90002, '6.75', '0.02');
$out = ['lane' => $lane, 'marker' => $marker,
    'natural_sha256' => $originalNatural, 'stages' => [],
    'parent_id' => 0, 'business_dml_test_only' => true,
    'token_deleted' => false, 'cleanup_zero' => false,
    'transaction_failure_injection' => 'not-tested-unsafe-to-force-on-nonstrict-MySQL'];
$issued = false;
$unsettled = false;
$id = 0;
$failed = false;
try {
    $stage = 'token-issue';
    if (!\think\facade\Cache::set($token, $actor, 300)
        || !\think\facade\Cache::has($token)) {
        throw new RuntimeException('Isolated File token unavailable');
    }
    $issued = true;

    $post = static function (string $action, array $data) use ($http, &$unsettled): array {
        $r = $http($action, $data);
        if ($r['http'] === 0 || !$r['json']) {
            $unsettled = true;
            throw new RuntimeException('POST transport or JSON state unknown');
        }
        return $r;
    };

    $stage = 'add-invalid-rules';
    $invalid = $addForm;
    $invalid['rules'] = [];
    $r = $post('add', ['form' => $invalid]);
    $check = $connect();
    $out['stages'][$stage] = $r;
    $out['stages'][$stage]['owned_rows'] = count($owned($check));
    $out['stages'][$stage]['natural_unchanged'] = $natural($check) === $originalNatural;
    if ((int)$r['status'] === 200 || count($owned($check)) !== 0
        || !$out['stages'][$stage]['natural_unchanged']) {
        throw new RuntimeException('Invalid add wrote data or returned success');
    }

    $stage = 'add';
    $r = $post('add', ['form' => $addForm]);
    $check = $connect();
    $parents = $owned($check);
    if (count($parents) === 1) $id = (int)$parents[0]['delivery_id'];
    $out['parent_id'] = $id;
    $out['stages'][$stage] = $r;
    $out['stages'][$stage]['natural_unchanged'] = $natural($check) === $originalNatural;
    if ($r['http'] !== 200 || (int)$r['status'] !== 200
        || $id <= 0 || !$out['stages'][$stage]['natural_unchanged']) {
        throw new RuntimeException('Add positive or ownership gate failed');
    }
    $out['stages'][$stage]['original_readback'] = $verifyState(
        $snapshotOwned($check, $id), $id, $marker, 0, '5.50', '0.01');

    $stage = 'edit-invalid-rules';
    $invalid = $editForm;
    $invalid['rules'] = [];
    $before = $snapshotOwned($connect(), $id);
    $r = $post('edit', ['deliveryId' => $id, 'form' => $invalid]);
    $check = $connect();
    $after = $snapshotOwned($check, $id);
    $out['stages'][$stage] = $r;
    $out['stages'][$stage]['owned_unchanged'] = $before['sha256'] === $after['sha256'];
    $out['stages'][$stage]['natural_unchanged'] = $natural($check) === $originalNatural;
    if ((int)$r['status'] === 200 || !$out['stages'][$stage]['owned_unchanged']
        || !$out['stages'][$stage]['natural_unchanged']) {
        throw new RuntimeException('Invalid edit wrote data or returned success');
    }

    $stage = 'edit';
    $r = $post('edit', ['deliveryId' => $id, 'form' => $editForm]);
    $check = $connect();
    $out['stages'][$stage] = $r;
    $out['stages'][$stage]['natural_unchanged'] = $natural($check) === $originalNatural;
    if ($r['http'] !== 200 || (int)$r['status'] !== 200
        || !$out['stages'][$stage]['natural_unchanged']) {
        throw new RuntimeException('Edit positive gate failed');
    }
    $out['stages'][$stage]['original_readback'] = $verifyState(
        $snapshotOwned($check, $id), $id, $marker . 'E', 0, '6.75', '0.02');

    $stage = 'delete';
    $check = $connect();
    if ($count($check, 'SELECT COUNT(*) FROM ' . $goodsTable
        . ' WHERE DELIVERY_ID=?', [$id]) !== 0) {
        throw new RuntimeException('Synthetic delivery unexpectedly referenced by goods');
    }
    $r = $post('delete', ['deliveryId' => $id]);
    $check = $connect();
    $out['stages'][$stage] = $r;
    $out['stages'][$stage]['natural_unchanged'] = $natural($check) === $originalNatural;
    if ($r['http'] !== 200 || (int)$r['status'] !== 200
        || !$out['stages'][$stage]['natural_unchanged']) {
        throw new RuntimeException('Delete positive gate failed');
    }
    $out['stages'][$stage]['original_readback'] = $verifyState(
        $snapshotOwned($check, $id), $id, $marker . 'E', 1, '6.75', '0.02');
} catch (Throwable $e) {
    $failed = true;
    $out['failure_stage'] = $stage;
    $out['failure_class'] = get_class($e);
} finally {
    if ($issued) {
        \think\facade\Cache::delete($token);
        $out['token_deleted'] = !\think\facade\Cache::has($token);
    }
    if ($unsettled) {
        $out['unsettled_request'] = true;
        $out['cleanup_deferred'] = true;
    } else {
        try {
            $stage = 'cleanup';
            $clean = $connect();
            $parents = $owned($clean);
            if (count($parents) > 1) {
                throw new RuntimeException('Ambiguous owned parent cleanup refused');
            }
            if (count($parents) === 1) {
                $cleanupId = (int)$parents[0]['delivery_id'];
                if ($id > 0 && $id !== $cleanupId) {
                    throw new RuntimeException('Owned parent ID changed');
                }
                $id = $cleanupId;
                $out['parent_id'] = $id;
                if ($count($clean, 'SELECT COUNT(*) FROM ' . $goodsTable
                    . ' WHERE DELIVERY_ID=?', [$id]) !== 0) {
                    throw new RuntimeException('Goods reference present; cleanup refused');
                }
                $clean->beginTransaction();
                try {
                    $childDelete = $clean->prepare('DELETE FROM ' . $ruleTable
                        . ' WHERE DELIVERY_ID=? AND STORE_ID=10001');
                    $childDelete->execute([$id]);
                    $parentDelete = $clean->prepare('DELETE FROM ' . $table
                        . ' WHERE DELIVERY_ID=? AND STORE_ID=10001 AND NAME IN (?,?)');
                    $parentDelete->execute([$id, $marker, $marker . 'E']);
                    if ($parentDelete->rowCount() !== 1) {
                        throw new RuntimeException('Exact parent cleanup affected-row mismatch');
                    }
                    $clean->commit();
                } catch (Throwable $e) {
                    $clean->rollBack();
                    throw $e;
                }
            }
            $verify = $connect();
            $out['cleanup_zero'] = count($owned($verify)) === 0
                && ($id === 0 || $count($verify,
                    'SELECT COUNT(*) FROM ' . $ruleTable
                    . ' WHERE DELIVERY_ID=?', [$id]) === 0);
            $out['final_natural_unchanged'] = $natural($verify) === $originalNatural;
        } catch (Throwable $e) {
            $out['cleanup_failure_class'] = get_class($e);
        }
    }
}
$out['pass'] = !$failed && !$unsettled && $out['token_deleted']
    && $out['cleanup_zero'] && ($out['final_natural_unchanged'] ?? false)
    && count($out['stages']) === 5;
echo json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
exit($out['pass'] ? 0 : 3);
