"""Send a retained provider request through an authenticated Chio process.

This worker never opens the host databases, issuer seed or broker credentials.
Keep the same operation key and prepared bytes when resolving the original call.
"""
import argparse
import json
import os
from pathlib import Path
import socket
import time

p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--connection', type=Path, required=True)
p.add_argument('--prepared', type=Path, required=True)
p.add_argument('--operation-key', required=True)
p.add_argument('--output', type=Path, required=True)
p.add_argument('--server', default='native-broker')
p.add_argument('--tool', default='send')
a = p.parse_args()
os.umask(0o077)

def load(path, limit):
    with path.open('rb') as f:
        data = f.read(limit + 1)
    if len(data) > limit:
        raise ValueError('input exceeds its byte limit')
    return json.loads(data)

def retain(name, data):
    fd = os.open(a.output/name, os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0o600)
    with os.fdopen(fd, 'wb') as f:
        f.write(data)
        f.flush()
        os.fsync(f.fileno())
    directory = os.open(a.output, os.O_RDONLY)
    try:
        os.fsync(directory)
    finally:
        os.close(directory)

def encoded(value):
    return json.dumps(value, separators=(',', ':'), ensure_ascii=False).encode()

connection = load(a.connection, 65_536)
if connection.get('schema') != 'chio.process.connection.v1':
    raise ValueError('expected the private descriptor from chio process credential')
prepared = load(a.prepared, 1_048_576)
request = {'operation_key':a.operation_key, 'server_id':a.server, 'tool_name':a.tool,
           'arguments':prepared, 'known_outcome_only':True}
context = {k:connection[k] for k in ('runtime_id','process_id','capability_id')}
a.output.mkdir(mode=0o700, parents=True, exist_ok=False)
parent_fd = os.open(a.output.parent, os.O_RDONLY)
try:
    os.fsync(parent_fd)
finally:
    os.close(parent_fd)
retain('request.json', encoded(request))
retain('context.json', encoded(context))
# This is the only payload containing the worker bearer. Never save or log it.
frame = encoded({'protocol':'chio.process.v1', 'credential':connection['credential'],
                 'operation':{'op':'invoke', **request}}) + b'\n'
if len(frame)>2*1024*1024:
    raise ValueError('worker request exceeds 2 MiB')
received = bytearray()
try:
    deadline = time.monotonic()+30
    with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as peer:
        peer.settimeout(max(0.001, deadline-time.monotonic()))
        peer.connect(connection['socket_path'])
        peer.settimeout(max(0.001, deadline-time.monotonic()))
        peer.sendall(frame)
        while b'\n' not in received:
            remaining = deadline-time.monotonic()
            if remaining<=0:
                raise TimeoutError('original call deadline elapsed')
            peer.settimeout(remaining)
            chunk = peer.recv(8192)
            if not chunk:
                raise ConnectionError('response was truncated')
            received.extend(chunk)
            if len(received)>8*1024*1024:
                raise ValueError('worker response exceeds 8 MiB')
    line = bytes(received).split(b'\n',1)[0]
    retain('wire-response.json', line)
    envelope = json.loads(line)
    if envelope.get('protocol')!='chio.process.v1' or envelope.get('ok') is not True:
        raise ValueError('host refused or returned an invalid envelope; inspect private wire-response.json')
    response = envelope['result']
    retain('response.json', encoded(response))
    receipt = response['receipt_json']
    if not isinstance(receipt,str):
        raise ValueError('missing original receipt bytes')
    retain('kernel-receipt.json', receipt.encode())
    terminal = response.get('terminal_state',{}).get('state')
    output = response.get('output')
    if response.get('verdict')=='allow' and terminal=='completed' and (
            not isinstance(output,dict) or output.get('kind')!='value'):
        raise ValueError('allowed call has no completed guarded value')
    print(json.dumps({'request_id':response.get('request_id'), 'verdict':response.get('verdict'),
                      'terminal_state':terminal, 'evidence':str(a.output)}))
    if response.get('verdict')!='allow' or terminal!='completed':
        raise SystemExit(2)
except Exception as error:
    # No new proof, invocation identity, credential use or provider retry here.
    retain('unresolved.txt', (str(error)+'\nRetain this original operation and prepared request.\n').encode())
    raise SystemExit('Original call lacks a usable completion; inspect the private evidence directory.') from None
