import ast
import datetime
import hashlib
import json
import os
from pathlib import Path
import re
import subprocess
import urllib.request

os.umask(0o077)
HERE = Path(__file__).parent
REPO = Path('/workspace/contextra-analytics-runtime-lifecycle-refactor')
JOB = '20260918-contextra-certificate-provision-r01'
ROOT = Path('/tmp/codex/ltb1021/git-closeout') / JOB
ROOT.mkdir(mode=0o700, parents=True, exist_ok=False)

def cmd(args, ok=(0,)):
    p = subprocess.run(args, text=True, capture_output=True, timeout=90)
    if p.returncode not in ok:
        raise RuntimeError('command failed: ' + args[0])
    return p.stdout

def save(path, value):
    with path.open('x') as f:
        json.dump(value, f, ensure_ascii=False, indent=2)
    path.chmod(0o600)

paths = [
    'tools/secure_bridge_provision.py', 'tests/test_secure_bridge_provision.py',
    'docs/integration/SECURE_BRIDGE_PROVISION.ko.md',
    'docs/integration/SECURE_REST_BRIDGE_HANDOFF.ko.md',
    'docs/profiles/ltb1021/topics/runtime-lifecycle/README.ko.md',
    'docs/profiles/ltb1021/topics/runtime-lifecycle/runs/20260918T112819+0900-run-certificate-provision.md',
]
assert not cmd(['git', '-C', str(REPO), 'diff', '--cached', '--name-only']).strip()
changed = cmd(['git', '-C', str(REPO), 'diff', '--name-only']).splitlines()
changed += cmd(['git', '-C', str(REPO), 'ls-files', '--others', '--exclude-standard']).splitlines()
assert set(changed) == set(paths)
cmd(['git', '-C', str(REPO), 'diff', '--check'])
links = 0
for name in paths:
    p = REPO / name
    if p.suffix == '.py':
        ast.parse(p.read_text())
    else:
        for target in re.findall(r'\]\(([^)]+)\)', p.read_text()):
            if '://' in target or target.startswith('#'):
                continue
            target = target.split('#', 1)[0]
            assert (p.parent / target).exists(), (name, target)
            links += 1
out = cmd(['python3', '-B', str(REPO / paths[0]), 'check', '--env-file',
           '/workspace/contextra-runtime-config/runtime.env'], ok=(2,))
preflight = json.loads(out)
containers = json.loads(cmd(['docker', 'inspect', 'contextra-analytics-1']))
c = containers[0]
env = dict(v.split('=', 1) for v in c['Config']['Env'])
with urllib.request.urlopen('http://127.0.0.1:35000/api/v1/streams', timeout=5) as r:
    streams = json.load(r)['streams']
result = dict(base_head=cmd(['git', '-C', str(REPO), 'rev-parse', 'HEAD']).strip(),
              preflight=preflight, tests='7 unittest PASS; completed before closeout',
              syntax='AST PASS', local_links=links, diff_check='PASS',
              runtime=dict(container_id=c['Id'], pid=c['State']['Pid'], image=c['Image'],
                           artifact_enabled=env.get('CONTEXTRA_EVENT_ARTIFACT_ENABLED'),
                           http_enabled=env.get('CONTEXTRA_ARTIFACT_HTTP_ENABLED'),
                           registrations=len(streams),
                           running=[s['stream_id'] for s in streams if s.get('state') == 'running']),
              external_login_calls=0, ca_installed=False, runtime_mutation=False,
              files={p: hashlib.sha256((REPO / p).read_bytes()).hexdigest() for p in paths})
save(HERE / 'result.json', result)
receipt = dict(schema_version='ltb1021.codex_hub.report_receipt.v1', receipt_id=JOB,
    timestamp=datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))).isoformat(),
    session_alias='work-session', task='Contextra 최초 인증서 설치 경계 검토와 현재 구조용 오프라인 준비 도구',
    results=['동료 스크립트는 승인 CA와 계정 선준비가 필요하며 WSS 발급 도구가 아님을 확인했다.',
             '현재 경로의 비변경 점검과 원자적 CA 설치를 기존 TLS 및 Config 검증에 연결했다.'],
    verify=['보호 검증 unittest7개 PASS, AST와 문서 링크 및 diff 검사 PASS.',
            '실제 입력 부족 exit2. 기존 Runtime과 두 채널 및 Artifact Off 유지, 실제 인증/배포 미실행.'],
    commits=[], remaining=['Manager 접속 정보와 승인 CA/CSR 서명 경로 확보 후 로그인/WSS 준비.'])
save(HERE / 'report-receipt.json', receipt)
print(cmd(['codex-report-receipt', '--profile', 'ltb1021', '--session-alias', 'work-session',
           '--receipt', str(HERE / 'report-receipt.json')]))
print(cmd(['python3', '/workspace/users/root/codex-ops-docs/servers/test/users/ltb1021/portfolio_logs/scripts/create_portfolio_candidate.py',
    '--run-dir', str(HERE), '--confidence', 'MEDIUM', '--repo', 'contextra-analytics',
    '--track-key', 'contextra-analytics:ops:deployment:reproducible-deployment', '--closeout-job', JOB,
    '--evidence-state', 'implemented', '--confidentiality', 'internal',
    '--reason', '오프라인 설치 준비 도구 구현과 보호 검증 완료; 실제 인증 입력과 연결 수용 대기',
    '--verify', '7 unittest PASS; actual preflight INPUT_REQUIRED; runtime unchanged']))
mf = ROOT / 'manifest.json'
args = ['codex-git-closeout', 'prepare', '--repository', str(REPO), '--output', str(mf),
    '--job-id', JOB, '--profile', 'ltb1021', '--session-alias', 'work-session',
    '--title', 'feat(secure-bridge): 현재 경로용 인증 준비 점검과 CA 설치 도구 추가',
    '--message', 'Why=동료 스크립트가 요구하는 초기 입력과 현행 배포 경계를 분리하고 인증 준비 완료 오판을 막는다.',
    '--message', 'What=비변경 입력 점검과 승인 CA 원자적 설치를 기존 TLS 및 Sidecar Config에 연결하고 사용 가이드와 재개 근거를 정리했다.',
    '--message', 'Impact=호스트 오프라인 도구만 추가했다. 기존 Runtime과 인증 계약 및 model/image/config는 변경하지 않았다.',
    '--message', 'Verify=unittest7개 및 AST/문서 링크/git diff --check PASS. 원본과 새 도구는 실제 인증 입력 부족에서 종료했고 외부 로그인/WSS는 미실행이다.',
    '--message', 'Rollback=기존 CA와 개인키를 교체하지 않는다. Git 이력과 원본 스크립트를 보존했고 자동 rollback이나 Runtime 재기동은 하지 않았다.',
    '--verify', '7 targeted unittest PASS; AST and local Markdown links PASS; git diff --check PASS',
    '--verify', 'actual INPUT_REQUIRED; no login/deploy; two streams running with Artifact Off',
    '--expires-in-seconds', '86400']
for name in paths:
    args += ['--path', name]
print(cmd(args))
plan = json.loads(mf.read_text())['plan_hash']
batch = ROOT / 'batch.json'
print(cmd(['codex-git-closeout', 'batch-prepare', '--output', str(batch), '--batch-id', JOB,
    '--profile', 'ltb1021', '--session-alias', 'work-session', '--expected-group-count', '1',
    '--repository-label', 'contextra-analytics', '--manifest', str(mf), '--expect-plan-hash', plan,
    '--expires-in-seconds', '86400']))
print('RESULT', json.dumps({k: result[k] for k in ('tests', 'local_links', 'runtime_mutation')}))
print('BATCH', batch.read_text())
