import hashlib
import importlib.util
import json
import os
from pathlib import Path
import shutil
import signal
import subprocess
import sys
import time

_spec = importlib.util.spec_from_file_location('prior_prepare', '/tmp/codex/ltb1021/2026-09-20_contextra-fall-ready_r01/prepare.py')
_prior = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_prior)
api, cmd, compose, inspect, ledger, REPO = (_prior.api, _prior.cmd, _prior.compose,
                                         _prior.inspect, _prior.ledger, _prior.REPO)

HERE = Path(__file__).parent
os.umask(0o077)

def save(name, value):
    with (HERE/name).open('x') as f:
        json.dump(value, f, indent=2)

def stopped():
    states = api('/api/v1/streams')['streams']
    assert all(s['state'] == 'stopped' and not s.get('child_pid') and not s.get('cleanup_pending') for s in states)
    return states

if __name__ == '__main__':
    assert not (HERE/'build-intent.json').exists(), 'Resume from intent; no duplicate build'
    states = stopped()
    names = cmd(['docker', 'ps', '-a', '--format', '{{.Names}}']).splitlines()
    before = {n: {k: inspect(n)[k] for k in ('Id', 'Image', 'State')} for n in names}
    save('before.json', dict(containers=before, ledger=ledger(), channels=len(states)))
    original = json.loads(cmd(compose()+['config', '--format', 'json']))
    save('compose-before.json', original)
    source = ('app/fall_config.py', 'app/fall_scenario.py')
    hashes = {p: hashlib.sha256((REPO/p).read_bytes()).hexdigest() for p in source}
    digest = hashlib.sha256(json.dumps(hashes, sort_keys=True).encode()).hexdigest()[:10]
    base = before['contextra-analytics-1']['Image']
    assert base == 'sha256:1b21eee481d8b8d32e06d9e9b4118c92c745e3d13ff11b22eb7942715a20b56d'
    save('build-intent.json', dict(hashes=hashes, base=base, digest=digest))
    snapshot = HERE/'snapshot'
    snapshot.mkdir()
    for path in source:
        dst = snapshot/path
        dst.parent.mkdir(exist_ok=True)
        shutil.copy2(REPO/path, dst)
    tag = 'contextra-analytics:fall-skip-hold-'+digest
    dockerfile = 'FROM '+base+'\nCOPY app/fall_config.py app/fall_scenario.py /opt/contextra/app/\n'
    lowest = shutil.disk_usage('/').free
    assert lowest > 15*1024**3
    with (HERE/'build.log').open('x') as log:
        p = subprocess.Popen(['docker', 'build', '--pull=false', '--network=none', '-t', tag,
                              '-f', '-', str(snapshot)], stdin=subprocess.PIPE,
                             stdout=log, stderr=log, text=True, start_new_session=True)
        p.stdin.write(dockerfile)
        p.stdin.close()
        start = time.monotonic()
        while p.poll() is None:
            lowest = min(lowest, shutil.disk_usage('/').free)
            if lowest < 15*1024**3 or time.monotonic()-start > 300:
                os.killpg(p.pid, signal.SIGTERM)
                p.wait(20)
                raise RuntimeError('BUILD_RESOURCE_BOUND')
            time.sleep(1)
    assert p.returncode == 0, 'Build failed; preserve partial output'
    image = json.loads(cmd(['docker', 'image', 'inspect', tag]))[0]['Id']
    save('image.json', dict(tag=tag, id=image, lowest_root_bytes=lowest))
    print(json.dumps(dict(tag=tag, id=image, lowest_root_gib=lowest/1024**3)), flush=True)
