import argparse
import json
from copy import deepcopy
from pathlib import Path


DOWNSTREAM_MESSAGE_VISIBILITIES = {"repo", "public"}


def _read_json(path):
    return json.loads(Path(path).read_text(encoding="utf-8-sig"))


def _filtered_thread(request):
    thread = deepcopy(request.get("thread", {}))
    messages = thread.get("messages", [])
    if isinstance(messages, list):
        thread["messages"] = [
            message
            for message in messages
            if isinstance(message, dict)
            and str(message.get("visibility", "repo")).strip().lower()
            in DOWNSTREAM_MESSAGE_VISIBILITIES
        ]
    else:
        thread["messages"] = []
    return thread


# Statuses whose meaning depends on the message body travelling with them.
# Terminal ones carry the reasoning for a decision; `needs_info` is worse — the
# recipient cannot answer a question whose body never arrived (REQ-131).
TERMINAL_STATUSES = {"implemented", "rejected", "deferred"}
BODY_DEPENDENT_STATUSES = TERMINAL_STATUSES | {"needs_info"}


def build_mailbox(repo_id, requests):
    """Scope requests to one origin repo, forwarding only what that repo may see.

    Withholding hub-only (`upstream`) messages is correct, but doing it SILENTLY
    made an empty delivery look successful: a resolution arrived as a status
    change with its reasoning stripped (REQ-130). The mailbox now reports what
    it withheld and names terminal requests whose reasoning was entirely
    hub-only, so a silent resolution is visible at publish time.
    """
    scoped_requests = []
    withheld = {}
    silent_resolutions = []
    for request in requests:
        if request.get("origin_repo") != repo_id:
            continue
        scoped = deepcopy(request)
        thread = _filtered_thread(request)
        scoped["thread"] = thread
        original = request.get("thread", {}).get("messages", [])
        original_count = len(original) if isinstance(original, list) else 0
        dropped = original_count - len(thread.get("messages", []))
        if dropped > 0:
            request_id = request.get("id", "<missing-id>")
            withheld[request_id] = dropped
            if (request.get("status") in BODY_DEPENDENT_STATUSES
                    and not thread.get("messages")):
                silent_resolutions.append(request_id)
        scoped_requests.append(scoped)
    return {"schema_version": "0.1", "repo_id": repo_id, "requests": scoped_requests,
            "withheld": withheld, "withheld_total": sum(withheld.values()),
            "silent_resolutions": silent_resolutions}


def main():
    parser = argparse.ArgumentParser(description="Publish a scoped downstream request mailbox.")
    parser.add_argument("--repo-id", required=True)
    parser.add_argument("--requests", default=".prd_plugin/state/requests.json")
    parser.add_argument("--output", required=True)
    args = parser.parse_args()

    data = _read_json(args.requests)
    mailbox = build_mailbox(args.repo_id, data.get("requests", []))
    output_path = Path(args.output)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(json.dumps(mailbox, indent=2) + "\n", encoding="utf-8")
    if mailbox["silent_resolutions"]:
        print("[PRD Plugin] WARNING: resolved with no deliverable reasoning "
              f"(hub-only replies): {', '.join(mailbox['silent_resolutions'])}. "
              "Reply with --visibility repo so the origin repo receives the body.")


if __name__ == "__main__":
    main()
