"""Prepare an office-report Mission from actual published metadata; does not execute it."""
import argparse
import json
from pathlib import Path

METHOD = "office_report.build_sales_report"
MAX_CSV_BYTES = 32768


def published_selection(metadata):
    if not isinstance(metadata, dict):
        raise ValueError("Expected ability show JSON object")
    fields = ("name", "owner_ura", "ability_ura", "descriptor_ref")
    if any(not isinstance(metadata.get(key), str) or not metadata[key].strip() for key in fields):
        raise ValueError("Need actual name, owner_ura, ability_ura and descriptor_ref from ability show")
    if metadata["name"] != METHOD:
        raise ValueError("Metadata is not the office_report.build_sales_report function")
    if metadata.get("admission_action") not in (None, "invoke"):
        raise ValueError("The report function requires RPC invocation")
    # This is consistency checking, not a replacement URA/descriptor parser.
    # The installed compiler must validate canonical owner/reference binding.
    if not metadata["descriptor_ref"].startswith(metadata["ability_ura"] + "@") or not metadata["descriptor_ref"].endswith("!invoke"):
        raise ValueError("Descriptor reference does not match the reported RPC ability")
    return metadata["owner_ura"], METHOD, metadata["descriptor_ref"]


def mission_source(metadata, csv_text):
    if not isinstance(csv_text, str) or len(csv_text.encode("utf-8")) > MAX_CSV_BYTES:
        raise ValueError("CSV must be UTF-8 text of at most 32 KiB")
    owner, method, pin = published_selection(metadata)
    q = json.dumps
    return ('mission "office-report-fixture" {\n'
            f'  let report = {q(owner)}.{q(method)}(csv_text: {q(csv_text)}) descriptor_ref {q(pin)} timeout 30 on_failure abort\n'
            '  emit "report" kind "answer" value report.output\n}\n')


def read_bounded(path, limit):
    with Path(path).open("rb") as source:
        payload = source.read(limit + 1)
    if len(payload) > limit:
        raise ValueError("Input exceeds allowed byte size")
    return payload.decode("utf-8")


def prepare(metadata_path, input_path, output_path):
    output_path = Path(output_path)
    if output_path.exists() or output_path.is_symlink():
        raise ValueError("Output already exists; choose a new Mission file")
    metadata = json.loads(read_bounded(metadata_path, 65536))
    source = mission_source(metadata, read_bounded(input_path, MAX_CSV_BYTES))
    # Exclusive creation also refuses a file created after the initial check.
    with output_path.open("x", encoding="utf-8") as output:
        output.write(source)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--descriptor", type=Path, required=True, help="Actual ability show --format json output")
    parser.add_argument("--input", type=Path, required=True, help="Local UTF-8 input.csv, at most 32 KiB")
    parser.add_argument("--output", type=Path, required=True, help="New Mission.eal; never overwrites")
    args = parser.parse_args()
    try:
        prepare(args.descriptor, args.input, args.output)
    except (ValueError, OSError) as error:
        # Do not echo raw metadata, inputs, identifiers or parser exception payloads.
        parser.exit(1, f"Mission preparation failed ({type(error).__name__}); check metadata, sizes and new output path.\n")
    print("Created Mission source. Run easynet mission compile <file> --emit-ir to validate it before execution.")


if __name__ == "__main__":
    main()
