"""Stage 6 — assemble manifest entries (spec §3.6). Pure dict transforms over a
loaded manifest dict; the caller does the json read/write. Idempotent by id so
re-running earlier stages never clobbers authored labels."""

from __future__ import annotations

import copy


def upsert_clip(manifest: dict, clip: dict) -> dict:
    """Replace the clip with the same id, or append it. Returns a new manifest."""
    m = copy.deepcopy(manifest)
    clips = m.setdefault("clips", [])
    for i, c in enumerate(clips):
        if c["id"] == clip["id"]:
            clips[i] = clip
            return m
    clips.append(clip)
    return m


def add_ring_scale(manifest: dict, scale_id: str, clip_id: str,
                   after: str | None = None) -> dict:
    """Insert a scale into ring.scales after the named scale (or append if `after`
    is None / not found). No-op if scale_id already present. Returns a new manifest."""
    m = copy.deepcopy(manifest)
    ring = m.setdefault("ring", {"scales": [], "transitions": []})
    scales = ring.setdefault("scales", [])
    if any(s["id"] == scale_id for s in scales):
        return m
    entry = {"id": scale_id, "clip_id": clip_id}
    idx = next((i for i, s in enumerate(scales) if s["id"] == after), None)
    if idx is None:
        scales.append(entry)
    else:
        scales.insert(idx + 1, entry)
    return m


def rebuild_ring_edges(manifest: dict) -> dict:
    """Rebuild ring.transitions to one placeholder entry per ring edge (N scales ->
    N edges incl. the wrap), preserving any existing model string for an edge.
    Returns a new manifest."""
    m = copy.deepcopy(manifest)
    ring = m.setdefault("ring", {"scales": [], "transitions": []})
    scales = ring.get("scales", [])
    existing = {t["file"]: t.get("model", "") for t in ring.get("transitions", [])}
    edges = []
    n = len(scales)
    for i in range(n):
        a, b = scales[i]["id"], scales[(i + 1) % n]["id"]
        file = f"transitions/{a}-{b}.mp4"
        edges.append({"file": file, "model": existing.get(file, "placeholder-zoom")})
    ring["transitions"] = edges
    return m
