"""Executor for RefSwapProposal.

Scope boundary: ref_resolver.py does not read shot["ref_overrides"] yet —
pipeline wiring is a separate follow-on (mirrors prompt_override pattern).
"""
from __future__ import annotations

import logging
from typing import Optional

from fastapi import HTTPException

from recoil.api.adapters import beats as beats_adapter
from recoil.api.eventbus import BUS

logger = logging.getLogger(__name__)

_SCOPE = "api/executors/ref_swap"


def execute(
    beat_id: str,
    swap_before: str,
    swap_after: str,
    prompt_additions: Optional[list[str]] = None,
    project_id: Optional[str] = None,
) -> dict:
    """Raises HTTPException(404) if beat_id not found on disk."""
    try:
        result = beats_adapter.set_ref_overrides(
            beat_id=beat_id,
            swap_before=swap_before,
            swap_after=swap_after,
            prompt_additions=prompt_additions,
            project_id=project_id,
        )
    except KeyError:
        BUS.emit_sync(
            severity="failure",
            scope=_SCOPE,
            summary=f"ref_swap_target_not_found: {beat_id}",
            payload={"beat_id": beat_id},
        )
        raise HTTPException(
            status_code=404,
            detail={
                "error": "beat_not_found",
                "beat_id": beat_id,
                "message": f"Cannot swap ref: beat {beat_id!r} not found on disk.",
            },
        )
    BUS.emit_sync(
        severity="success",
        scope=_SCOPE,
        summary=f"ref_swap_applied: {beat_id}",
        payload={
            **result,
            "beat_id": beat_id,
        },
    )
    return result
