"""Exercise one configured, sender-attesting Chio process host.

The owner supplies three separately authenticated ProcessClients. This module
does not mint credentials, start workers, or authenticate a message signature.
Run it once against a fresh jobs mailbox and a new private evidence directory.
"""
import json
import os
from pathlib import Path
from chio_process import ProcessClient, WorkerError


def save(path, text):
    descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
    with os.fdopen(descriptor, "w", encoding="utf-8") as stream:
        stream.write(text)
        stream.flush()
        os.fsync(stream.fileno())
    descriptor = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
    try:
        os.fsync(descriptor)
    finally:
        os.close(descriptor)


def invoke_recorded(client, evidence, name, operation_key, tool, arguments):
    # Freeze the exact request before dispatch. A transport failure is unresolved.
    request = {"operation_key": operation_key, "server_id": "chio-ipc",
               "tool_name": tool, "arguments": arguments}
    save(evidence / f"{name}.request.json", json.dumps(request, sort_keys=True) + "\n")
    try:
        response = client.invoke(operation_key, "chio-ipc", tool, arguments)
    except WorkerError as error:
        save(evidence / f"{name}.unresolved.json", json.dumps({"error": error.code}) + "\n")
        raise  # Retain the original request; never choose a new key to retry it.
    save(evidence / f"{name}.response.json", json.dumps(response, sort_keys=True) + "\n")
    receipt = response["receipt_json"]
    if not isinstance(receipt, str) or "\n" in receipt:
        raise ValueError("Expected one original signed receipt")
    save(evidence / f"{name}.receipts.jsonl", receipt + "\n")
    return response


def completed(response):
    if response["verdict"] != "allow" or response["terminal_state"]["state"] != "completed":
        raise RuntimeError("Original operation requires attention")
    output = response["output"]
    if not output or output["kind"] != "value" or output["value"].get("isError"):
        raise RuntimeError("Expected a completed mailbox value")
    return output["value"]


def exercise_mailbox(writer: ProcessClient, other_writer: ProcessClient,
                     reviewer: ProcessClient, evidence: Path):
    evidence.mkdir(mode=0o700)  # Existing evidence is a conflict, not a reset.
    if writer.inspect()["process_id"] != "fixer" or other_writer.inspect()["process_id"] != "tester":
        raise ValueError("Wrong independently supplied writer routes")
    if reviewer.inspect()["process_id"] != "reviewer":
        raise ValueError("Wrong independently supplied reviewer route")
    payload = {"fix": "issue-417", "declared_sender": "tester"}
    arguments = {"message_key": "issue-417", "payload": payload}
    original = invoke_recorded(writer, evidence, "original", "issue-417/send/v1", "send_jobs", arguments)
    sequence = completed(original)["sequence"]
    retry = invoke_recorded(writer, evidence, "retry", "issue-417/send/v1", "send_jobs", arguments)
    if retry != original:
        raise RuntimeError("Exact retry did not return the original response and receipt")
    resend = invoke_recorded(writer, evidence, "resend", "issue-417/resend/v1", "send_jobs", arguments)
    if completed(resend)["sequence"] != sequence:
        raise RuntimeError("Same message key created another sequence")
    received = invoke_recorded(reviewer, evidence, "receive", "issue-417/receive/v1",
                               "receive_jobs", {"after_sequence": "0", "limit": 10})
    messages = completed(received)["messages"]
    if len(messages) != 1 or messages[0]["sender"] != "fixer" or messages[0]["payload"] != payload:
        raise RuntimeError("Payload label replaced kernel attribution, or another effect appeared")
    changed = invoke_recorded(writer, evidence, "changed", "issue-417/changed/v1", "send_jobs",
                              {"message_key": "issue-417", "payload": {"fix": "issue-418"}})
    impersonated = invoke_recorded(other_writer, evidence, "other-sender", "issue-417/other/v1",
                                   "send_jobs", arguments)
    conflict = "tool server error: mailbox message key or claim conflicts with the recorded payload, sender or claimant"
    for response in (changed, impersonated):
        if response["verdict"] != "deny" or response["output"] is not None or response["reason"] != conflict:
            raise RuntimeError("Expected the exact signed mailbox conflict")
    ack = invoke_recorded(reviewer, evidence, "ack", "issue-417/ack/v1", "ack_jobs",
                         {"through_sequence": sequence})
    if completed(ack)["status"] != "acknowledged":
        raise RuntimeError("Acknowledgment did not complete")
    after_ack = invoke_recorded(writer, evidence, "after-ack", "issue-417/after-ack/v1", "send_jobs", arguments)
    if completed(after_ack)["sequence"] != sequence:
        raise RuntimeError("Acknowledgment forgot the original message binding")
    expired = invoke_recorded(reviewer, evidence, "old-cursor", "issue-417/old-cursor/v1",
                              "receive_jobs", {"after_sequence": "0", "limit": 10})
    if completed(expired) != {"status": "cursor_expired", "acknowledged_through": sequence}:
        raise RuntimeError("Expected the actual acknowledged cursor boundary")
    empty = invoke_recorded(reviewer, evidence, "empty", "issue-417/empty/v1",
                            "receive_jobs", {"after_sequence": sequence, "limit": 10})
    if completed(empty)["messages"]:
        raise RuntimeError("Resend after acknowledgment recreated consumed work")
    print("ATTRIBUTE fixer: payload's declared_sender cannot select the kernel sender")
    print("REPLAY original response; exact resend keeps the original sequence")
    print("REFUSE changed payload and other sender with signed mailbox conflicts")
    print("ACK original sequence; later exact resend creates no visible message")
    print("CURSOR before the acknowledged sequence expires; continue from the retained sequence")
    return original
