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


LOCAL_STATE_MARKERS = (".prd_plugin/local", ".prd_plugin\\local")
UPSTREAM_MESSAGE_VISIBILITIES = {"upstream", "public"}
WINDOWS_ABSOLUTE_PATH = re.compile(r"^[A-Za-z]:[\\/]")
POSIX_LOCAL_PREFIXES = ("/Users/", "/home/")


def _contains_local_state_reference(value):
    return any(marker in str(value) for marker in LOCAL_STATE_MARKERS)


def _is_local_path(value):
    path = str(value)
    return (
        _contains_local_state_reference(path)
        or WINDOWS_ABSOLUTE_PATH.match(path) is not None
        or path.startswith(POSIX_LOCAL_PREFIXES)
    )


def _sanitized_source_refs(request):
    source_refs = request.get("source_refs", [])
    if not isinstance(source_refs, list):
        return []

    sanitized = []
    for source_ref in source_refs:
        if not isinstance(source_ref, dict):
            continue
        if _is_local_path(source_ref.get("path", "")):
            continue
        sanitized.append(deepcopy(source_ref))
    return sanitized


def _sanitized_messages(request):
    thread = request.get("thread", {})
    if not isinstance(thread, dict):
        return []

    messages = thread.get("messages", [])
    if not isinstance(messages, list):
        return []

    sanitized = []
    for message in messages:
        if not isinstance(message, dict):
            continue
        visibility = str(message.get("visibility", "repo")).strip().lower()
        if visibility not in UPSTREAM_MESSAGE_VISIBILITIES:
            continue
        if _contains_local_state_reference(message):
            continue
        sanitized.append(deepcopy(message))
    return sanitized


def build_upstream_submission(request, upstream_hub):
    submission = deepcopy(request)
    submission["source_request_id"] = request.get("id")
    submission["scope"] = "upstream_submission"
    submission["upstream_hub"] = upstream_hub
    submission["upstream_submission"] = True
    submission["visibility"] = "repo"
    submission["upstream_request_id"] = request.get("upstream_request_id")
    submission["source_refs"] = _sanitized_source_refs(request)

    thread = submission.get("thread", {})
    if not isinstance(thread, dict):
        thread = {}
    thread["messages"] = _sanitized_messages(request)
    submission["thread"] = thread

    return submission


# Delivering to a PEER is delivering to a repo, so it carries the repo-facing
# set — not the hub-facing one. Keeping the two directions as separate named
# constants is deliberate: reusing the upstream filter for peer traffic silently
# strips message bodies, which is exactly how nine replies were delivered empty
# (REQ-131). `upstream` means hub-private and must never travel to a peer.
PEER_MESSAGE_VISIBILITIES = {"repo", "public"}


def build_peer_package(request, origin_repo):
    """The package a peer repo imports when this repo addresses it (REQ-136).

    Same sanitization as an upstream submission — local state references are
    stripped, never leaked across a repo boundary — but the repo-facing
    visibility set, because the recipient is a repo.
    """
    package = deepcopy(request)
    package["source_request_id"] = request.get("id")
    package["origin_repo"] = origin_repo
    package["scope"] = "peer_submission"
    package["peer_submission"] = True
    package["visibility"] = "repo"
    package["source_refs"] = _sanitized_source_refs(request)

    thread = package.get("thread", {})
    if not isinstance(thread, dict):
        thread = {}
    messages = []
    for message in (request.get("thread") or {}).get("messages", []):
        if not isinstance(message, dict):
            continue
        visibility = str(message.get("visibility", "repo")).strip().lower()
        if visibility not in PEER_MESSAGE_VISIBILITIES:
            continue
        if _contains_local_state_reference(message):
            continue
        messages.append(deepcopy(message))
    thread["messages"] = messages
    package["thread"] = thread
    return package


def _load_requests(path):
    data = json.loads(Path(path).read_text(encoding="utf-8"))
    requests = data.get("requests", [])
    if not isinstance(requests, list):
        raise ValueError(f"{path} must contain a 'requests' array")
    return requests


def _find_request(requests, request_id):
    for request in requests:
        if request.get("id") == request_id:
            return request
    raise ValueError(f"request {request_id} was not found")


def main():
    parser = argparse.ArgumentParser(
        description="Create a sanitized upstream submission from a local request."
    )
    parser.add_argument("--requests", default=".prd_plugin/state/requests.json")
    parser.add_argument("--request-id", required=True)
    parser.add_argument("--upstream-hub", default="prd-plugin")
    parser.add_argument("--output", help="Output package path. Defaults to .prd_plugin/outbox/<request-id>-upstream.json.")
    args = parser.parse_args()

    request = _find_request(_load_requests(args.requests), args.request_id)
    submission = build_upstream_submission(request, args.upstream_hub)

    output_path = Path(args.output or f".prd_plugin/outbox/{args.request_id}-upstream.json")
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(json.dumps(submission, indent=2) + "\n", encoding="utf-8")


if __name__ == "__main__":
    main()
