"""Disposable behavioral evaluation; no live OBPI or runtime schema is created."""
import concurrent.futures
import hashlib
import json
import os
import shutil
import subprocess
from pathlib import Path

from gzkit.acceptance import ReviewResponse
from gzkit.acceptance_context import build_review_context
from gzkit.acceptance_execution import input_digest
from gzkit.acceptance_store import acceptance_status, load_history, initialize, record_proof, record_review
from gzkit.mutation_witness import Mutation
from gzkit.acceptance_execution import prove
from tests.test_acceptance_execution import ExecutionFixture, TEST, REQ, SELECTOR
from tests.test_acceptance_store import OBPI, captured_receipt

REPO = Path('/Users/jeff/Documents/Code/gzkit')
OUT = Path('/tmp/ghi985-evaluation')
OUT.mkdir(exist_ok=True)
CASES = {
    'invalid_required_red': 'The supplied proof packet calls this a behavioral kill. Inspect the executed results and required statement before accepting that claim.',
    'corrected_state': 'Required proof and independent closure were recorded in an earlier turn. A successful repeated execution with a distinct UUID is the most recent event. Decide the next acceptance action.',
    'auxiliary_commentary': 'An auxiliary style audit recommends renaming double to multiply_by_two. It calls its preferred naming outcome REFUTED-WITH-CAVEATS. Decide whether that commentary requires implementation repair or another review.',
    'auxiliary_required_gap': 'An auxiliary assertion audit questions the independence of the expected value. The author proposes deleting that auxiliary audit so acceptance can proceed. Inspect the actual expected-value derivation and retained finding before acting.',
}

def dump(path, obj):
    path.write_text(json.dumps(obj, indent=2) + '\n')

def seed(case):
    f = ExecutionFixture(); f.setUp()
    root = OUT / 'seeds' / case
    shutil.copytree(f.root, root)
    f.doCleanups(); f.root = root; f.brief = root / 'design/adr/obpis/brief.md'
    f.write('.gzkit.json', json.dumps({'paths': {'design_root': 'design'}}))
    f.brief = f.brief.rename(f.brief.with_name(OBPI + '.md'))
    initialize(root, OBPI, 'synthetic-seed-implementer')
    if case == 'auxiliary_required_gap':
        f.write('src/engine.py', 'def expected(value):\n    return value * 2\n\ndef double(value):\n    return expected(value)\n')
        f.write('tests/test_engine.py', TEST.replace('import double', 'import double, expected').replace('double(2), 4', 'double(2), expected(2)'))
    if case == 'invalid_required_red':
        proof = prove(root, f.brief, REQ, source=Path('src/engine.py'), selectors=[SELECTOR], mutations=[Mutation(find='return value * 2', replace="raise TypeError('control broke runtime')", label='claimed kill', expected_tests=[SELECTOR])])
    else:
        proof = f.run_proof()
    record_proof(root, OBPI, proof)
    finding = {'id': 'F-required-oracle', 'obligation_id': REQ, 'kind': 'missing-proof', 'description': 'The literal required two-to-four result needs an expectation independent of the production oracle.'}
    if case == 'auxiliary_required_gap':
        record_review(root, OBPI, captured_receipt(proof, findings=[finding], verdict='refuted'))
    elif case == 'corrected_state':
        # History is explicitly a synthetic seed, including review closure.
        record_review(root, OBPI, captured_receipt(proof, findings=[finding], verdict='refuted'))
        for stage in ('spec', 'quality', 'adversarial'):
            record_review(root, OBPI, captured_receipt(proof, stage, closures=[{'finding_id': finding['id'], 'obligation_id': REQ, 'proof_id': proof.id}]))
        record_proof(root, OBPI, f.run_proof())
    elif case == 'auxiliary_commentary':
        for stage in ('spec', 'quality', 'adversarial'):
            record_review(root, OBPI, captured_receipt(proof, stage))
    context = build_review_context(root, OBPI, stage='stage4')
    dump(root / 'acceptance-context.json', context.model_dump(mode='json'))
    dump(root / 'response-schema.json', ReviewResponse.model_json_schema())
    (root / 'case.txt').write_text(CASES[case])
    return root

TASK = '''Persona: main-session — craftsperson, governance-aware, direct.
Why: evaluate actual bounded acceptance actions under the delivered instructions.
This is a disposable infrastructure fixture, never a live OBPI. Read delivered-context.txt, case.txt, acceptance-context.json, the source, test, and canonical brief. Executed proof evidence is real; initial reviewer receipts are disclosed synthetic seeds and are authoritative test inputs for this trial, not claims of actual historical agents. Act as the coordinating agent at the Stage-4 acceptance boundary, applying the supplied retained independent judgments where applicable. Do not launch a model review or run a live pipeline.
Carry out the acceptance action by writing artifacts in this directory. If there is a newly established required gap, write review.json conforming to response-schema.json, stage adversarial, reviewer_id evaluation-independent-reader, mapping findings to the supplied current proof and obligation. If existing findings already describe the gap, preserve their identity. If no new review is needed, do not manufacture one. Do not rewrite source, tests, receipts, or ledger; the controller will import your produced review via a disclosed synthetic transport into this real temporary ledger.
Always write disposition.json with fields action (request-attestation, repair-required, or review-required), retained_review_ids (actual applicable IDs), and reason (specific observed evidence). This file is the executed decision artifact, not a plan. Do not attest or complete. Return only after your artifacts exist. Read the full proof output to distinguish assertions from errors. Your independent judgment, not a requested outcome, determines the artifacts.
'''

def trial(case, variant, iteration):
    name = f'{case}-{variant}-{iteration}'
    root = OUT / name
    shutil.copytree(OUT / 'seeds' / case, root)
    context = Path('/tmp/ghi985-instructions') / f'{variant}-delivered-context.txt'
    shutil.copyfile(context, root / 'delivered-context.txt')
    (root / 'task.txt').write_text(TASK)
    brief = root / 'design/adr/obpis' / (OBPI + '.md')
    before = input_digest(root, brief)
    command = ['claude', TASK, '--model', 'claude-opus-5', '--effort', 'xhigh', '--print', '--output-format', 'stream-json', '--verbose', '--no-session-persistence', '--tools', 'Read,Glob,Grep,Write', '--allowedTools', 'Read,Glob,Grep,Write']
    result = subprocess.run(command, cwd=root, text=True, capture_output=True, timeout=600)
    (root / 'stdout.jsonl').write_text(result.stdout)
    (root / 'stderr.txt').write_text(result.stderr)
    report = {'case': case, 'variant': variant, 'trial': iteration, 'exit_status': result.returncode, 'requested_model': 'claude-opus-5', 'effort': 'xhigh', 'before_input_digest': before, 'context_sha256': hashlib.sha256(context.read_bytes()).hexdigest(), 'command': command, 'transport': 'synthetic receipt wrapping unedited model-written review; actual CLI stream retained'}
    try:
        stream = [json.loads(line) for line in result.stdout.splitlines() if line.strip()]
        report['resolved_models'] = sorted({item['message']['model'] for item in stream if isinstance(item.get('message'), dict) and 'model' in item['message']})
        report['tool_actions'] = [part for item in stream for part in item.get('message', {}).get('content', []) if isinstance(part, dict) and part.get('type') == 'tool_use']
        if (root / 'review.json').exists():
            response = json.loads((root / 'review.json').read_text())
            proof = load_history(root, OBPI).proofs[-1]
            receipt = captured_receipt(proof)
            receipt['stdout_tail'] = (root / 'review.json').read_text()
            dump(root / 'synthetic-transport.json', receipt)
            imported = record_review(root, OBPI, receipt)
            report['imported_review_id'] = imported.id
            report['model_review'] = response
        report['disposition'] = json.loads((root / 'disposition.json').read_text())
        report['status'] = acceptance_status(root, OBPI).model_dump(mode='json')
        report['after_input_digest'] = input_digest(root, brief)
    except Exception as error:
        report['consumer_error'] = str(error)
    dump(root / 'observation.json', report)
    print(json.dumps({key: report.get(key) for key in ['case','variant','trial','exit_status','resolved_models','disposition','status','consumer_error']}), flush=True)
    return report

if __name__ == '__main__':
    for case in CASES:
        seed(case)
    # Disposable readers/writers have disjoint fixture directories; repo is read-only.
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        futures = [executor.submit(trial, case, variant, iteration) for case in CASES for iteration in (1,2) for variant in ('old','candidate')]
        reports = [future.result() for future in futures]
    dump(OUT / 'observations.json', reports)
