#!/usr/bin/env python3
"""Root checks real wire types, positive database transitions and preserved failures."""
import base64
import hashlib
import json
import subprocess
from pathlib import Path
import integrate_20261010 as tool
from integrate_20261011_batches46_48 import item

HERE = Path(__file__).resolve().parent
FIN = tool.ROOT.parent / 'worktrees/dm8-compat-20261012/private-prepay-harness'
STORE = Path('/Users/wiscourper/Documents/workspace/local/dm8-store-purchase-read3-20261012.L5dTYR/stage3/bill-add-fixed204-20261013')
AI_BASE = 'work_ai_api/2026-09-30-dm8-interface-acceptance/parallel-h5-zero-confirm-20261013/'
STORE_BASE = 'work_store/apifox/2026-09-30-dm8-interface-acceptance/parallel-bill-add-time-fixed-20261013/'
PHP = '/opt/homebrew/opt/php@7.4/bin/php'


def shape(value):
    if isinstance(value, dict):
        return ('object', [(k, shape(v)) for k, v in value.items()])
    if isinstance(value, list):
        return ('array', [shape(v) for v in value])
    return type(value).__name__


def sealed_run(root, report_sha, verifier, verifier_sha):
    assert hashlib.sha256((root / 'REPORT.md').read_bytes()).hexdigest() == report_sha
    assert hashlib.sha256((root / verifier).read_bytes()).hexdigest() == verifier_sha
    return json.loads(subprocess.check_output([PHP, str(root / verifier)]))


def verify201():
    root = FIN / 'batch201-h5-confirm-zero'
    r = sealed_run(root, '09a70cb6a40469fd9a77c598968a1cbb5c1b1bebda3e18617528d6868cf57248', 'verify_offline.php', '2e54d7acc6eb8abbc336303da8ae05ac737d9ba6a1e4b0818f17047ec39cf99e')
    assert r['checks'] == 81 and r['business_http'] == 4 and r['unique_route_keys'] == 1
    assert r['business_dml_per_engine'] == dict(bill_update=1, zero_confirm_log_insert=1, repeat_dml=0)
    for phase in ('first', 'repeat'):
        bodies = [json.loads((root / f'{lane}-201-confirmzero-{phase}.raw').read_text()) for lane in ('B', 'C')]
        assert shape(bodies[0]) == shape(bodies[1]), 'Check real JSON types before masking identities'
        for b in bodies:
            assert type(b['data']['bill_id']) is int and type(b['data']['bill']['id']) is int
            assert type(b['data']['bill']['record_id']) is int
    source = tool.source_root('ai_api')
    assert hashlib.sha256((source / 'app/api/service/H5Bill.php').read_bytes()).hexdigest() == 'a903f672432780e58e023d91b81fdf2bf5a8d62a629061007cfae5c5d24c1aab'
    assert "Route::rule('h5/bills/:billId/confirm-zero', 'api/H5/billConfirmZero', 'POST')" in (source / 'route/app.php').read_text()


def verify204():
    r = sealed_run(STORE, 'ca0a883b50d346dd7266b2e6830c87fe3a48b2eee272145fa4273abf50b8db20', 'verify204.php', '6c483ad707f1f05b9515bc4c3a5751f678569208436742454e342ba0176f09dc')
    assert r['checks'] == 70
    manifest = STORE / 'manifest204.json'
    assert hashlib.sha256(manifest.read_bytes()).hexdigest() == '4f65fb9adcbf363414066796659d7d11f205c1c64488efae3aa4e17fd48898ed'
    m = json.loads(manifest.read_text())
    for asset in m['assets']:
        p = STORE / asset['path']
        assert not p.is_symlink() and hashlib.sha256(p.read_bytes()).hexdigest() == asset['sha256']
    for lane, qty in [('B', 2), ('C', 3)]:
        assert r['sides'][lane]['HTTP'] == 1 and r['sides'][lane]['qty'] == qty
        assert r['sides'][lane]['other82_strict_same'] and r['sides'][lane]['cleanup']


def verify205():
    root = FIN / 'batch205-h5-confirm-alias'
    r = sealed_run(root, 'c29e939b0b4cbab1aa0c486c97854a8a13e9ef9e075c5a7473b37f3ecdfe23ef', 'verify_offline.php', '6ee60d60e25536bbda5d6381f046d27debe0edd58eeadefce58ac8dfc53c980f')
    assert r['checks'] == 24 and r['business_http'] == 2 and r['business_dml'] == 0
    for lane in ('B', 'C'):
        a = json.loads((root / f'{lane}-205-h5-confirm-alias.raw').read_text())
        b = json.loads((FIN / 'batch201-h5-confirm-zero' / f'{lane}-201-confirmzero-repeat.raw').read_text())
        assert shape(a) == shape(b)


def verify206():
    root = FIN / 'batch206-h5-detail-alias'
    r = sealed_run(root, '7414359db406ffdb31b35b394e5c698b55542899bb76c27d62cad4acf94055ae', 'verify_offline.php', '3f1c2119e0d487f40e3a01f452ba14861eaa5aa9eaaa4b1819d9ce576d5da879')
    assert r['checks'] == 23 and r['business_http'] == 2 and r['business_dml'] == 0
    bodies = [json.loads((root / f'{lane}-206-h5-detail-alias.raw').read_text()) for lane in ('B', 'C')]
    assert shape(bodies[0]) == shape(bodies[1])


if __name__ == '__main__':
    new = item('ai_api', 'POST', '/api/h5/bills/:billId/confirm-zero', AI_BASE,
        success_case='partial-evidence-real-zero-confirm-20-to30-and-repeat-no-write-not-bank-payment',
        readback='tested-pass-owned-bill-conditional-U1-log-I1-and-repeat-no-DML',
        duplicate_retry='tested-pass-original-paid-branch-zero-writes-and-one-log',
        register_new_runtime_key=True, runtime_observed_method='POST',
        runtime_inventory_scope='controller-direct-http-observed', runtime_direct_http_proof=AI_BASE+'REPORT.md',
        runtime_controller_source='ai_api/route/app.php:35 app/api/controller/H5.php:85 actual original HTTP')
    restored = item('store', 'POST', '/p/bill/add', STORE_BASE,
        success_case='partial-evidence-new-date-count-recount-and-restored-automatic-update-time',
        readback='tested-pass-original-recipe-log-quantity-and-noop-real-change-date0-side-effects',
        parity='partial-evidence-identical-original-HTTP-and-new-source-time-semantics',
        cleanup='own-new-date-data-retained-exact-new-workers-token-cache-removed-old195-preserved')
    alias = item('ai_api', 'POST', '/api/H5/billConfirmZero', AI_BASE,
        success_case='partial-evidence-direct-action-alias-paid-read-only-branch-not-initial-confirm',
        readback='tested-pass-existing-owned-paid-bill-log-and-users-unchanged',
        duplicate_retry='partial-evidence-direct-alias-already-confirmed-zero-DML')
    detail = item('ai_api', 'GET', '/api/H5/billDetail', AI_BASE,
        success_case='partial-evidence-direct-action-alias-nonempty-owned-zero-bill',
        readback='tested-pass-owned-bill-detail-log-and-users-unchanged')
    batches = [(201, new, verify201), (204, restored, verify204), (205, alias, verify205), (206, detail, verify206)]
    for number, _, verify in batches:
        assert not (HERE / f'integration-20261013-batch{number}.json').exists()
        verify()
    for number, target, verify in batches:
        tool.main([target], HERE / f'integration-20261013-batch{number}.json', verify)
