"""Owner review of parked inbound items (F9) — the verbs over ``review_store``.

Parked items are machine JSON in ``review_store``; these functions list them and, by
id, either APPROVE (file the intent) or REJECT (drop it). Both mutate the queue and let
``review_store`` re-render the human view; git preserves a rejected item in history.

APPROVE is an **owner override**: the peer's grant no longer gates it (the owner is
sovereign over their own vault, and every approval is a commit), but the path
sanitization in ``apply.file_intent`` always applies — approve can never file an unsafe
path. Items merely held for first-contact review file straight through; an out-of-scope
item files because the owner said so.
"""
import datetime as dt
from pathlib import Path

from alexandria_vault.federation import apply as fed_apply, review_store


def _peer_by_fp(cfg, fp):
    return next((p for p in (cfg.get("peers") or []) if p.get("fingerprint") == fp), None)


def list_items(vault):
    """Every parked item still awaiting review: id, peer, reason, label, preview."""
    return [{"id": it.get("id"),
             "peer": it.get("peer_handle") or it.get("peer_name"),
             "peer_fp": it.get("peer_fp"),
             "reason": it.get("reason"),
             "received": it.get("received"),
             "label": review_store._label(it.get("intent") or {}),
             "capture": it.get("capture"),
             "preview": review_store._readable(it.get("intent") or {})}
            for it in review_store.all_items(vault)]


def _receipt(item, status, label, *, now=None):
    """Queue the receipt that closes a captured item (needs the envelope id the
    capture recorded; a legacy parked item without one sends nothing)."""
    eid, fp = item.get("eid"), item.get("peer_fp")
    if not (eid and fp):
        return False
    from alexandria_vault.federation import envelope, outbox
    body = (envelope.receipt_body(eid, "applied", [label], []) if status == "applied"
            else envelope.receipt_body(eid, "declined", [], [label]))
    outbox.enqueue_receipt(fp, body, now=now)
    return True


def _retire_capture(vault, item):
    rel = item.get("capture")
    if not rel:
        return
    try:
        (Path(vault) / rel).unlink()
    except OSError:
        pass


def approve(vault, cfg, iid, *, today=None, commit=None):
    """File a parked item by id (owner override) and drop it from the queue. Returns a
    result dict: ``approved`` | ``not-found`` | ``no-peer`` (peer since removed) |
    ``blocked`` (unsafe path — stays parked). ``commit`` (if given) is called with a
    message after the vault write."""
    today = today or dt.date.today().isoformat()
    item = review_store.get(vault, iid)
    if item is None:
        return {"status": "not-found", "id": iid}
    peer = _peer_by_fp(cfg, item.get("peer_fp"))
    if peer is None:
        return {"status": "no-peer", "id": iid, "peer_fp": item.get("peer_fp")}
    res = fed_apply.file_intent(vault, peer, item.get("intent") or {}, cfg, today,
                                enforce_permits=False)          # owner override
    if res["status"] != "applied":
        # Sanitization refused it even under override (e.g. an unsafe path) — leave it
        # parked and report why, rather than silently dropping the item.
        return {"status": "blocked", "id": iid, "reason": res.get("reason")}
    # File first, THEN dequeue — deliberately favouring no-loss over no-dup: a crash in
    # this window leaves the item both filed and parked (a re-approve appends a duplicate,
    # which the owner can see and delete), never filed-but-lost-from-the-queue.
    review_store.remove(vault, iid)
    _retire_capture(vault, item)
    receipt = _receipt(item, "applied", res.get("label") or "approved")
    if commit:
        commit(f"federation: approved {iid} ({res.get('label')}) "
               f"from {peer.get('name') or 'a peer'}")
    return {"status": "approved", "id": iid, "label": res.get("label"),
            "path": res.get("path"), "peer": peer.get("handle"),
            "capture": item.get("capture"), "receipt": "queued" if receipt else None}


def reject(vault, iid, *, commit=None):
    """Drop a parked item by id without filing it (git preserves the history). Returns
    ``rejected`` | ``not-found``. The capture file is retired and, when the item carries its
    envelope id, a ``declined`` receipt is queued for the next tick — the sender learns
    the push was reviewed and not filed, never why."""
    item = review_store.remove(vault, iid)
    if item is None:
        return {"status": "not-found", "id": iid}
    label = review_store._label(item.get("intent") or {})
    _retire_capture(vault, item)
    receipt = _receipt(item, "declined", label)
    if commit:
        commit(f"federation: rejected {iid} ({label})")
    return {"status": "rejected", "id": iid, "label": label,
            "capture": item.get("capture"), "receipt": "queued" if receipt else None}
