"""Dual-publish codegen.* (legacy) and run.* (canonical) WebSocket events.

When a run blocks on manifest values, the worker emits **only**
``run.user_input_required`` (no ``codegen.completed`` / ``run.completed``) so clients get a single
event. Legacy ``codegen.awaiting_user_input`` is not emitted.
"""
from __future__ import annotations
from typing import Any, Optional

from redis import asyncio as aioredis

from src.core.pubsub import publish_event
from src.core.public_output import redact_public_output

# codegen_event -> run_event (None = no canonical alias)
CODEGEN_TO_RUN_EVENT: dict[str, Optional[str]] = {
    "codegen.started": "run.started",
    "codegen.status_changed": "run.progress",
    "codegen.ai_output": "run.progress",
    "codegen.plan_ready": "run.progress",
    "codegen.clarification_required": "run.clarification_required",
    "codegen.completed": "run.completed",
    "codegen.failed": "run.failed",
    "git.push_started": "run.progress",
    "git.push_completed": "run.progress",
    "preview.provisioning": "run.progress",
}

STATUS_MESSAGES: dict[str, tuple[str, str]] = {
    "pending": ("queued", "Queued. We will start in a moment."),
    "queued": ("queued", "Queued. We will start in a moment."),
    "planning": ("planning", "Understanding your request."),
    "running": ("generating", "Working on your app."),
    "generating": ("generating", "Writing the changes."),
    "applying": ("applying", "Applying the changes."),
    "validating": ("validating", "Checking that everything still works."),
    "preview_refreshing": ("preview", "Preparing the preview."),
    "completed": ("completed", "Done."),
    "completed_with_limitations": ("completed_with_limitations", "Done, with a limitation the platform couldn't fully support."),
    "blocked": ("blocked", "A requested capability isn't available on the platform yet."),
    "failed": ("failed", "Something went wrong."),
    "canceled": ("canceled", "Canceled."),
    "plan_ready": ("plan_ready", "Plan ready for review."),
    "clarification_required": ("clarification_required", "A few details are needed before we can continue."),
    "user_input_required": ("user_input_required", "Configuration values are needed before we can finish."),
    "awaiting_user_input": ("user_input_required", "Configuration values are needed before we can finish."),
}


def _enrich_run_payload(codegen_event: str, data: dict[str, Any]) -> dict[str, Any]:
    out = dict(data)
    if "text" in out and isinstance(out["text"], str):
        out["text"] = redact_public_output(out["text"])
    for key in ("message", "display_message"):
        if isinstance(out.get(key), str):
            out[key] = redact_public_output(out[key])
    if codegen_event == "codegen.plan_ready":
        out.setdefault("stage", "plan_ready")
        out.setdefault("message", "Plan ready for review.")
    elif codegen_event == "codegen.clarification_required":
        out.setdefault("stage", "clarification_required")
        out.setdefault("message", "More information is needed to continue.")
    elif codegen_event == "codegen.ai_output":
        out.setdefault("stage", "generating")
    elif codegen_event == "git.push_started":
        out.setdefault("stage", "git")
        # Neutral copy — avoid surfacing vendor/hosting names in the live stream UI.
        out.setdefault("message", "Saving changes…")
    elif codegen_event == "git.push_completed":
        out.setdefault("stage", "git")
        out.setdefault("message", "Changes saved.")
    elif codegen_event == "preview.provisioning":
        out.setdefault("stage", "preview")
        out.setdefault("message", "Preparing preview…")
    elif codegen_event == "codegen.status_changed":
        status = str(out.get("status") or "running")
        stage, message = STATUS_MESSAGES.get(status, (status, "Working on your app."))
        out.setdefault("stage", stage)
        out.setdefault("message", message)
        out.setdefault("display_message", message)
        out.setdefault("role", "system")
    return out


async def publish_project_event_dual(
    redis: aioredis.Redis,
    project_id: str,
    codegen_event: str,
    data: dict[str, Any],
) -> None:
    """Emit legacy ``codegen.*`` (or other worker events) plus canonical ``run.*`` alias.

    High-volume stream lines use **only** ``run.progress`` (no duplicate ``codegen.ai_output``).
    """
    # Sanitize every browser-facing event at the single pub/sub boundary. This
    # covers both the legacy event and its canonical run.* alias.
    data = dict(data)
    for key in ("text", "message", "display_message"):
        if isinstance(data.get(key), str):
            data[key] = redact_public_output(data[key])
    run_ev = CODEGEN_TO_RUN_EVENT.get(codegen_event)
    if codegen_event == "codegen.ai_output" and run_ev:
        await publish_event(redis, project_id, run_ev, _enrich_run_payload(codegen_event, data))
        return
    await publish_event(redis, project_id, codegen_event, data)
    if run_ev:
        await publish_event(redis, project_id, run_ev, _enrich_run_payload(codegen_event, data))
