from __future__ import annotations

import csv
from collections import Counter
import json
import math
import re
from pathlib import Path
from typing import Any, Iterable


CONTRACT_VERSION = 1
MAX_RESULTS = 500
MAX_ID_LENGTH = 80
MAX_TITLE_LENGTH = 100
MAX_REASON_LENGTH = 800
MAX_NOTE_LENGTH = 400
MAX_QUOTE_LENGTH = 500
BOUNDARY_WORD_RADIUS = 8
HIGHLIGHT_HANDLE_BEFORE_MS = 150
HIGHLIGHT_HANDLE_AFTER_MS = 250
YOUTUBE_MIN_CHAPTERS = 3
YOUTUBE_MIN_SECTION_MS = 10_000
HIGHLIGHT_TYPES = (
    "Teaser",
    "Highlight",
    "Pull Quote",
    "Overview",
    "Reveal",
    "Reaction",
    "Social Cutdown",
)
ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$")
PREMIERE_REF_RE = re.compile(r"::(?:SEQ|PI|BIN|TRACK|ITEM|MARKER)\b", re.IGNORECASE)
ABSOLUTE_PATH_RE = re.compile(
    r"(?:^|[\s\"'])/(?!/)[^\s]+|[A-Za-z]:[\\/]|\\\\[^\\\s]+\\"
)
MARKER_COLUMNS = (
    "markerId",
    "resultId",
    "name",
    "type",
    "startSeconds",
    "durationSeconds",
    "comments",
)
SUMMARY_COLUMNS = (
    "workflow",
    "runId",
    "markerCount",
    "coverageStatus",
    "compatibilityWarningCount",
)
INVENTORY_COLUMNS = (
    "markerRef", "index", "name", "type", "startSeconds", "durationSeconds", "comments",
)


def canonical_json(value: Any) -> str:
    return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))


def _object(value: Any, label: str) -> dict[str, Any]:
    if not isinstance(value, dict):
        raise ValueError(f"{label} must be an object.")
    return value


def _array(value: Any, label: str) -> list[Any]:
    if not isinstance(value, list):
        raise ValueError(f"{label} must be an array.")
    return value


def _safe_id(value: Any, label: str) -> str:
    if not isinstance(value, str) or not ID_RE.fullmatch(value):
        raise ValueError(f"{label} must be a safe ID of at most {MAX_ID_LENGTH} characters.")
    return value


def _safe_text(
    value: Any,
    label: str,
    *,
    maximum: int,
    allow_empty: bool = False,
) -> str:
    if not isinstance(value, str):
        raise ValueError(f"{label} must be a string.")
    text = re.sub(r"\s+", " ", value).strip()
    shown = repr(text[:160] + ("…" if len(text) > 160 else ""))
    if not text and not allow_empty:
        raise ValueError(f"{label} must not be empty; rejected value: {shown}.")
    if len(text) > maximum:
        raise ValueError(f"{label} exceeds {maximum} characters; rejected value: {shown}.")
    if any(ord(character) < 32 for character in text):
        raise ValueError(f"{label} contains unsupported control characters; rejected value: {shown}.")
    if PREMIERE_REF_RE.search(text) or ABSOLUTE_PATH_RE.search(text):
        raise ValueError(
            f"{label} contains a Premiere reference or absolute path; rejected value: {shown}."
        )
    return text


def _optional_text(value: Any, label: str, *, maximum: int) -> str:
    if value is None:
        return ""
    return _safe_text(value, label, maximum=maximum, allow_empty=True)


def _reject_extra_keys(value: dict[str, Any], allowed: set[str], label: str) -> None:
    extras = sorted(set(value) - allowed)
    if extras:
        raise ValueError(f"{label} contains unsupported fields: {', '.join(extras)}.")


def chapter_response_schema() -> dict[str, Any]:
    return {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "additionalProperties": False,
        "required": ["contractVersion", "chapters"],
        "properties": {
            "contractVersion": {"type": "integer", "const": CONTRACT_VERSION},
            "chapters": {
                "type": "array",
                "maxItems": MAX_RESULTS,
                "items": {
                    "type": "object",
                    "additionalProperties": False,
                    "required": [
                        "chapterId",
                        "startUnitId",
                        "endUnitId",
                        "title",
                        "reason",
                        "uncertainty",
                    ],
                    "properties": {
                        "chapterId": {"type": "string", "pattern": ID_RE.pattern, "maxLength": MAX_ID_LENGTH},
                        "startUnitId": {"type": "string", "pattern": ID_RE.pattern, "maxLength": MAX_ID_LENGTH},
                        "endUnitId": {
                            "type": ["string", "null"],
                            "maxLength": MAX_ID_LENGTH,
                        },
                        "title": {"type": "string", "minLength": 1, "maxLength": MAX_TITLE_LENGTH},
                        "reason": {"type": "string", "minLength": 1, "maxLength": MAX_REASON_LENGTH},
                        "uncertainty": {"type": ["string", "null"], "maxLength": MAX_NOTE_LENGTH},
                    },
                },
            },
        },
    }


def highlight_response_schema() -> dict[str, Any]:
    return {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "additionalProperties": False,
        "required": ["contractVersion", "candidates"],
        "properties": {
            "contractVersion": {"type": "integer", "const": CONTRACT_VERSION},
            "candidates": {
                "type": "array",
                "maxItems": MAX_RESULTS,
                "items": {
                    "type": "object",
                    "additionalProperties": False,
                    "required": [
                        "candidateId",
                        "startUnitId",
                        "endUnitId",
                        "score",
                        "type",
                        "title",
                        "representativeQuote",
                        "reason",
                        "intendedUse",
                        "uncertainty",
                        "handleNote",
                    ],
                    "properties": {
                        "candidateId": {"type": "string", "pattern": ID_RE.pattern, "maxLength": MAX_ID_LENGTH},
                        "startUnitId": {"type": "string", "pattern": ID_RE.pattern, "maxLength": MAX_ID_LENGTH},
                        "endUnitId": {"type": "string", "pattern": ID_RE.pattern, "maxLength": MAX_ID_LENGTH},
                        "score": {"type": "integer", "minimum": 1, "maximum": 100},
                        "type": {"type": "string", "enum": list(HIGHLIGHT_TYPES)},
                        "title": {"type": "string", "minLength": 1, "maxLength": MAX_TITLE_LENGTH},
                        "representativeQuote": {"type": "string", "minLength": 1, "maxLength": MAX_QUOTE_LENGTH},
                        "reason": {"type": "string", "minLength": 1, "maxLength": MAX_REASON_LENGTH},
                        "intendedUse": {"type": "string", "minLength": 1, "maxLength": MAX_NOTE_LENGTH},
                        "uncertainty": {"type": ["string", "null"], "maxLength": MAX_NOTE_LENGTH},
                        "handleNote": {"type": ["string", "null"], "maxLength": MAX_NOTE_LENGTH},
                    },
                },
            },
        },
    }


def global_agent_request(
    request: dict[str, Any],
    *,
    workflow: str,
    density: str = "Automatic",
    emphasis: str = "Balanced",
    candidate_count: str = "Automatic",
) -> dict[str, Any]:
    if request.get("workflow") != workflow:
        raise ValueError("Prepared request workflow does not match the requested contract.")
    if workflow == "chapters":
        if density not in {"Automatic", "Fewer", "More"}:
            raise ValueError("Chapter density is unsupported.")
        system_prompt = (
            "You are a senior video editor creating useful viewer-facing YouTube chapters "
            "from a complete transcript-only view of one edited sequence. Analyze the whole "
            "story before proposing boundaries. Return only the requested JSON. Select supplied "
            "unit IDs, never timestamps. Titles must be concise, specific, and publication-ready. "
            "Every chapter object must contain every field required by the output schema and no "
            "additional fields. endUnitId is the final supplied unit that belongs to the chapter; "
            "set it to null only when no meaningful end can be determined. Set uncertainty to "
            "null when there is no uncertainty. "
            "Do not invent weak chapters to hit a count. Pauses, edit boundaries, and parallel "
            "groups are evidence, not mandatory cuts. When transcript coverage is incomplete, "
            "stay honest about what cannot be inferred visually or from missing speech."
        )
        editorial_instruction = {
            "density": density,
            "goal": "Propose an ordered chapter structure for navigation and publishing.",
            "firstChapter": (
                "Choose the earliest meaningful transcript chapter. The deterministic layer will "
                "anchor the first marker and chapter-text line to sequence time 0."
            ),
        }
    elif workflow == "highlights":
        if emphasis not in {"Balanced", "Teasers", "Highlights", "Social cutdowns"}:
            raise ValueError("Highlight emphasis is unsupported.")
        if candidate_count not in {"Automatic", "Fewer", "More"}:
            raise ValueError("Highlight candidate count is unsupported.")
        system_prompt = (
            "You are a senior video editor identifying the strongest transcript-based highlights "
            "and teaser moments across one complete edited sequence. Compare the entire story "
            "before ranking candidates. Return only the requested JSON and use supplied unit IDs, "
            "never timestamps. Prefer useful editorial options over filler; returning fewer "
            "candidates is allowed. Every candidate object must contain every field required by "
            "the output schema and no additional fields; set uncertainty and handleNote to null "
            "when they do not apply. "
            "Overlapping candidates are allowed when their editorial use "
            "meaningfully differs. Scores are reasoned suggestions, not objective truth. Pauses, "
            "edit boundaries, and parallel groups are context clues, not mandatory cuts."
        )
        editorial_instruction = {
            "emphasis": emphasis,
            "candidateCount": candidate_count,
            "goal": (
                "Propose high-value moments for teasers, highlights, pull quotes, overviews, "
                "reveals, reactions, or social cutdowns."
            ),
        }
    else:
        raise ValueError("workflow must be 'chapters' or 'highlights'.")
    user_payload = {
        "editorialInstruction": editorial_instruction,
        "sequenceContext": request,
    }
    return {
        "contractVersion": CONTRACT_VERSION,
        "workflow": workflow,
        "systemPrompt": system_prompt,
        "userPrompt": json.dumps(user_payload, ensure_ascii=False, indent=2),
        "outputSchema": (
            chapter_response_schema() if workflow == "chapters" else highlight_response_schema()
        ),
    }


def _unit_index(sidecar: dict[str, Any]) -> tuple[list[dict[str, Any]], dict[str, int]]:
    units = _array(sidecar.get("units"), "timingSidecar.units")
    by_id: dict[str, int] = {}
    for index, raw_unit in enumerate(units):
        unit = _object(raw_unit, f"timingSidecar.units[{index}]")
        unit_id = _safe_id(unit.get("id"), f"timingSidecar.units[{index}].id")
        if unit_id in by_id:
            raise ValueError(f"Timing sidecar contains duplicate unit ID {unit_id}.")
        by_id[unit_id] = index
    return units, by_id


def _word_index(sidecar: dict[str, Any]) -> tuple[list[dict[str, Any]], dict[str, int]]:
    words = _array(sidecar.get("words"), "timingSidecar.words")
    by_id: dict[str, int] = {}
    for index, raw_word in enumerate(words):
        word = _object(raw_word, f"timingSidecar.words[{index}]")
        word_id = _safe_id(word.get("wordId"), f"timingSidecar.words[{index}].wordId")
        if word_id in by_id:
            raise ValueError(f"Timing sidecar contains duplicate word ID {word_id}.")
        by_id[word_id] = index
    return words, by_id


def validate_global_response(
    sidecar: dict[str, Any],
    response: dict[str, Any],
    *,
    workflow: str,
) -> dict[str, Any]:
    _object(sidecar, "timingSidecar")
    response = _object(response, "response")
    if response.get("contractVersion") != CONTRACT_VERSION:
        raise ValueError("Response contractVersion is unsupported.")
    units, unit_positions = _unit_index(sidecar)
    del units
    if workflow == "chapters":
        _reject_extra_keys(response, {"contractVersion", "chapters"}, "response")
        raw_results = _array(response.get("chapters"), "response.chapters")
        if len(raw_results) > MAX_RESULTS:
            raise ValueError(f"Response exceeds the structural limit of {MAX_RESULTS} chapters.")
        seen_ids: set[str] = set()
        seen_starts: set[str] = set()
        previous_position = -1
        chapters: list[dict[str, Any]] = []
        validation_warnings: list[dict[str, str]] = []
        for index, raw_result in enumerate(raw_results):
            result = _object(raw_result, f"chapters[{index}]")
            _reject_extra_keys(
                result,
                {"chapterId", "startUnitId", "endUnitId", "title", "reason", "uncertainty"},
                f"chapters[{index}]",
            )
            chapter_id = _safe_id(result.get("chapterId"), f"chapters[{index}].chapterId")
            if chapter_id in seen_ids:
                raise ValueError(f"Duplicate chapter ID {chapter_id}.")
            start_unit_id = _safe_id(result.get("startUnitId"), f"chapters[{index}].startUnitId")
            if start_unit_id not in unit_positions:
                raise ValueError(f"Unknown chapter start unit ID {start_unit_id}.")
            if start_unit_id in seen_starts:
                raise ValueError(f"Duplicate chapter start unit ID {start_unit_id}.")
            position = unit_positions[start_unit_id]
            if position <= previous_position:
                raise ValueError("Chapter starts must be in strictly ascending story order.")
            raw_end_unit_id = result.get("endUnitId")
            end_unit_id = ""
            if raw_end_unit_id is not None:
                try:
                    proposed_end_unit_id = _safe_id(
                        raw_end_unit_id,
                        f"chapters[{index}].endUnitId",
                    )
                except ValueError:
                    validation_warnings.append(
                        {
                            "reasonCode": "CHAPTER_END_UNIT_IGNORED_INVALID_ID",
                            "chapterId": chapter_id,
                        }
                    )
                else:
                    if proposed_end_unit_id not in unit_positions:
                        validation_warnings.append(
                            {
                                "reasonCode": "CHAPTER_END_UNIT_IGNORED_UNKNOWN_ID",
                                "chapterId": chapter_id,
                            }
                        )
                    elif unit_positions[proposed_end_unit_id] < position:
                        validation_warnings.append(
                            {
                                "reasonCode": "CHAPTER_END_UNIT_IGNORED_BEFORE_START",
                                "chapterId": chapter_id,
                            }
                        )
                    else:
                        end_unit_id = proposed_end_unit_id
            chapters.append(
                {
                    "chapterId": chapter_id,
                    "startUnitId": start_unit_id,
                    "endUnitId": end_unit_id,
                    "title": _safe_text(result.get("title"), f"chapters[{index}].title", maximum=MAX_TITLE_LENGTH),
                    "reason": _safe_text(result.get("reason"), f"chapters[{index}].reason", maximum=MAX_REASON_LENGTH),
                    "uncertainty": _optional_text(result.get("uncertainty"), f"chapters[{index}].uncertainty", maximum=MAX_NOTE_LENGTH),
                }
            )
            seen_ids.add(chapter_id)
            seen_starts.add(start_unit_id)
            previous_position = position
        return {
            "contractVersion": CONTRACT_VERSION,
            "chapters": chapters,
            "validationWarnings": validation_warnings,
        }

    if workflow != "highlights":
        raise ValueError("workflow must be 'chapters' or 'highlights'.")
    _reject_extra_keys(response, {"contractVersion", "candidates"}, "response")
    raw_results = _array(response.get("candidates"), "response.candidates")
    if len(raw_results) > MAX_RESULTS:
        raise ValueError(f"Response exceeds the structural limit of {MAX_RESULTS} candidates.")
    seen_ids: set[str] = set()
    candidates: list[dict[str, Any]] = []
    rejected_candidates: list[dict[str, str]] = []
    for index, raw_result in enumerate(raw_results):
        result = _object(raw_result, f"candidates[{index}]")
        _reject_extra_keys(
            result,
            {
                "candidateId", "startUnitId", "endUnitId", "score", "type", "title",
                "representativeQuote", "reason", "intendedUse", "uncertainty", "handleNote",
            },
            f"candidates[{index}]",
        )
        candidate_id = _safe_id(result.get("candidateId"), f"candidates[{index}].candidateId")
        if candidate_id in seen_ids:
            raise ValueError(f"Duplicate candidate ID {candidate_id}.")
        start_unit_id = _safe_id(result.get("startUnitId"), f"candidates[{index}].startUnitId")
        end_unit_id = _safe_id(result.get("endUnitId"), f"candidates[{index}].endUnitId")
        if start_unit_id not in unit_positions or end_unit_id not in unit_positions:
            raise ValueError(f"Candidate {candidate_id} contains an unknown unit ID.")
        if unit_positions[start_unit_id] > unit_positions[end_unit_id]:
            raise ValueError(f"Candidate {candidate_id} has a reversed unit range.")
        score = result.get("score")
        if isinstance(score, bool) or not isinstance(score, int) or not 1 <= score <= 100:
            raise ValueError(f"Candidate {candidate_id} score must be an integer from 1 through 100.")
        candidate_type = result.get("type")
        if candidate_type not in HIGHLIGHT_TYPES:
            raise ValueError(f"Candidate {candidate_id} has an unsupported type.")
        try:
            candidate = {
                "candidateId": candidate_id,
                "startUnitId": start_unit_id,
                "endUnitId": end_unit_id,
                "score": score,
                "type": candidate_type,
                "title": _safe_text(result.get("title"), f"candidates[{index}].title", maximum=MAX_TITLE_LENGTH),
                "representativeQuote": _safe_text(result.get("representativeQuote"), f"candidates[{index}].representativeQuote", maximum=MAX_QUOTE_LENGTH),
                "reason": _safe_text(result.get("reason"), f"candidates[{index}].reason", maximum=MAX_REASON_LENGTH),
                "intendedUse": _safe_text(result.get("intendedUse"), f"candidates[{index}].intendedUse", maximum=MAX_NOTE_LENGTH),
                "uncertainty": _optional_text(result.get("uncertainty"), f"candidates[{index}].uncertainty", maximum=MAX_NOTE_LENGTH),
                "handleNote": _optional_text(result.get("handleNote"), f"candidates[{index}].handleNote", maximum=MAX_NOTE_LENGTH),
            }
        except ValueError as error:
            raw_title = result.get("title")
            shown_title = re.sub(r"\s+", " ", raw_title).strip() if isinstance(raw_title, str) else repr(raw_title)
            rejected_candidates.append(
                {
                    "candidateId": candidate_id,
                    "title": shown_title[:160] + ("…" if len(shown_title) > 160 else ""),
                    "reasonCode": "UNSAFE_MARKER_TEXT",
                    "reason": str(error),
                }
            )
        else:
            candidates.append(candidate)
        seen_ids.add(candidate_id)
    return {
        "contractVersion": CONTRACT_VERSION,
        "candidates": candidates,
        "rejectedCandidates": rejected_candidates,
    }


def _word_options(
    words: list[dict[str, Any]],
    center: int,
) -> list[dict[str, str]]:
    start = max(0, center - BOUNDARY_WORD_RADIUS)
    end = min(len(words), center + BOUNDARY_WORD_RADIUS + 1)
    return [
        {"wordId": str(word["wordId"]), "text": str(word["text"])}
        for word in words[start:end]
    ]


def boundary_response_schema(workflow: str) -> dict[str, Any]:
    properties: dict[str, Any] = {
        "resultId": {"type": "string", "pattern": ID_RE.pattern, "maxLength": MAX_ID_LENGTH},
        "startWordId": {"type": "string", "pattern": ID_RE.pattern, "maxLength": MAX_ID_LENGTH},
    }
    required = ["resultId", "startWordId"]
    if workflow == "highlights":
        properties["endWordId"] = {"type": "string", "pattern": ID_RE.pattern, "maxLength": MAX_ID_LENGTH}
        required.append("endWordId")
    elif workflow != "chapters":
        raise ValueError("workflow must be 'chapters' or 'highlights'.")
    return {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "additionalProperties": False,
        "required": ["contractVersion", "refinements"],
        "properties": {
            "contractVersion": {"type": "integer", "const": CONTRACT_VERSION},
            "refinements": {
                "type": "array",
                "maxItems": MAX_RESULTS,
                "items": {
                    "type": "object",
                    "additionalProperties": False,
                    "required": required,
                    "properties": properties,
                },
            },
        },
    }


def boundary_agent_request(
    sidecar: dict[str, Any],
    validated: dict[str, Any],
    *,
    workflow: str,
) -> dict[str, Any] | None:
    units, unit_positions = _unit_index(sidecar)
    words, word_positions = _word_index(sidecar)
    results = validated["chapters"] if workflow == "chapters" else validated["candidates"]
    if not results:
        return None
    entries: list[dict[str, Any]] = []
    for result in results:
        result_id = result["chapterId"] if workflow == "chapters" else result["candidateId"]
        start_unit = units[unit_positions[result["startUnitId"]]]
        start_center = word_positions[str(start_unit["startWordId"])]
        entry: dict[str, Any] = {
            "resultId": result_id,
            "startOptions": _word_options(words, start_center),
            "originalStartWordId": str(start_unit["startWordId"]),
        }
        if workflow == "highlights":
            end_unit = units[unit_positions[result["endUnitId"]]]
            end_center = word_positions[str(end_unit["endWordId"])]
            entry["endOptions"] = _word_options(words, end_center)
            entry["originalEndWordId"] = str(end_unit["endWordId"])
        entries.append(entry)
    system_prompt = (
        "You are refining transcript-only edit boundaries. For every supplied result, choose "
        "exactly one start word ID"
        + (" and one end word ID" if workflow == "highlights" else "")
        + " from that result's own allowed options. Keep the original boundary when no option "
        "is clearly better. Return only the requested JSON with top-level contractVersion as "
        "the integer 1. Every refinement must contain every field required by the output schema "
        "and no additional fields. Never invent IDs or timestamps."
    )
    return {
        "contractVersion": CONTRACT_VERSION,
        "workflow": workflow,
        "systemPrompt": system_prompt,
        "userPrompt": json.dumps({"results": entries}, ensure_ascii=False, indent=2),
        "outputSchema": boundary_response_schema(workflow),
        "entries": entries,
    }


def validate_boundary_response(
    boundary_request: dict[str, Any] | None,
    response: dict[str, Any] | None,
    *,
    workflow: str,
) -> dict[str, dict[str, str]]:
    if boundary_request is None:
        if response is not None:
            raise ValueError("Boundary response was supplied without a boundary request.")
        return {}
    if response is None:
        return {}
    response = _object(response, "boundaryResponse")
    _reject_extra_keys(response, {"contractVersion", "refinements"}, "boundaryResponse")
    if response.get("contractVersion") != CONTRACT_VERSION:
        raise ValueError("Boundary response contractVersion is unsupported.")
    raw_refinements = _array(response.get("refinements"), "boundaryResponse.refinements")
    entries = {entry["resultId"]: entry for entry in boundary_request["entries"]}
    if len(raw_refinements) != len(entries):
        raise ValueError("Boundary response must contain exactly one refinement per result.")
    validated: dict[str, dict[str, str]] = {}
    for index, raw_refinement in enumerate(raw_refinements):
        refinement = _object(raw_refinement, f"refinements[{index}]")
        allowed_keys = {"resultId", "startWordId"}
        if workflow == "highlights":
            allowed_keys.add("endWordId")
        _reject_extra_keys(refinement, allowed_keys, f"refinements[{index}]")
        result_id = _safe_id(refinement.get("resultId"), f"refinements[{index}].resultId")
        if result_id not in entries:
            raise ValueError(f"Unknown boundary result ID {result_id}.")
        if result_id in validated:
            raise ValueError(f"Duplicate boundary result ID {result_id}.")
        start_word_id = _safe_id(refinement.get("startWordId"), f"refinements[{index}].startWordId")
        allowed_starts = {option["wordId"] for option in entries[result_id]["startOptions"]}
        if start_word_id not in allowed_starts:
            raise ValueError(f"Boundary start for {result_id} escaped its allowed window.")
        value = {"startWordId": start_word_id}
        if workflow == "highlights":
            end_word_id = _safe_id(refinement.get("endWordId"), f"refinements[{index}].endWordId")
            allowed_ends = {option["wordId"] for option in entries[result_id]["endOptions"]}
            if end_word_id not in allowed_ends:
                raise ValueError(f"Boundary end for {result_id} escaped its allowed window.")
            value["endWordId"] = end_word_id
        validated[result_id] = value
    return validated


def _milliseconds(value: Any, label: str) -> int:
    if isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(value):
        raise ValueError(f"{label} must be a finite number.")
    return round(float(value))


def _seconds_text(milliseconds: int) -> str:
    return f"{milliseconds / 1000:.3f}".rstrip("0").rstrip(".") or "0"


def format_chapter_time(milliseconds: int) -> str:
    if milliseconds < 0:
        raise ValueError("Chapter time must not be negative.")
    total_seconds = milliseconds // 1000
    hours = total_seconds // 3600
    minutes = total_seconds % 3600 // 60
    seconds = total_seconds % 60
    if milliseconds >= 3_600_000:
        return f"{hours}:{minutes:02d}:{seconds:02d}"
    return f"{total_seconds // 60}:{seconds:02d}"


def format_chapter_lines(entries: Iterable[dict[str, Any]]) -> str:
    lines: list[str] = []
    for index, entry in enumerate(entries):
        title = _safe_text(
            entry.get("title", ""),
            f"chapterEntries[{index}].title",
            maximum=MAX_TITLE_LENGTH,
            allow_empty=True,
        )
        if not title:
            continue
        milliseconds = _milliseconds(
            entry.get("startMs"),
            f"chapterEntries[{index}].startMs",
        )
        lines.append(f"{format_chapter_time(milliseconds)} {title}")
    return "\n".join(lines)


def _validated_coverage_summary(coverage: dict[str, Any]) -> dict[str, Any]:
    coverage = _object(coverage, "coverageReport")
    status = coverage.get("status")
    if status not in {"READY", "READY_WITH_WARNINGS"}:
        raise ValueError("coverageReport.status is unsupported.")
    excluded = coverage.get("excludedItemCount")
    if isinstance(excluded, bool) or not isinstance(excluded, int) or excluded < 0:
        raise ValueError("coverageReport.excludedItemCount must be non-negative.")
    percent = coverage.get("mappedSpeechPercentOfSequence")
    if isinstance(percent, bool) or not isinstance(percent, (int, float)) or not math.isfinite(percent):
        raise ValueError("coverageReport.mappedSpeechPercentOfSequence must be finite.")
    if not 0 <= percent <= 100:
        raise ValueError("coverageReport.mappedSpeechPercentOfSequence must be between 0 and 100.")
    return {
        "status": status,
        "excludedItemCount": excluded,
        "mappedSpeechPercentOfSequence": percent,
    }


def _coverage_note(coverage: dict[str, Any]) -> str:
    if coverage.get("status") == "READY":
        return ""
    excluded = coverage.get("excludedItemCount", 0)
    return f"Transcript coverage warning: {excluded} eligible item(s) were unavailable or unsupported."


def _marker_comment(lines: Iterable[str]) -> str:
    cleaned = [line.strip() for line in lines if isinstance(line, str) and line.strip()]
    comment = "\n".join(cleaned)
    if len(comment) > 4000:
        raise ValueError("Marker comment exceeds the structural limit of 4000 characters.")
    return comment


def materialize_marker_plan(
    sidecar: dict[str, Any],
    coverage: dict[str, Any],
    validated: dict[str, Any],
    *,
    workflow: str,
    run_id: str,
    boundary_response: dict[str, Any] | None = None,
) -> dict[str, Any]:
    run_id = _safe_id(run_id, "runId")
    sequence = _object(sidecar.get("sequence"), "timingSidecar.sequence")
    sequence_start_ms = _milliseconds(sequence.get("startMs"), "sequence.startMs")
    sequence_end_ms = _milliseconds(sequence.get("endMs"), "sequence.endMs")
    if sequence_start_ms < 0 or sequence_end_ms <= sequence_start_ms:
        raise ValueError("Timing sidecar sequence range is invalid.")
    units, unit_positions = _unit_index(sidecar)
    words, word_positions = _word_index(sidecar)
    boundary_request = boundary_agent_request(sidecar, validated, workflow=workflow)
    refinements = validate_boundary_response(
        boundary_request,
        boundary_response,
        workflow=workflow,
    )
    markers: list[dict[str, Any]] = []
    warnings: list[dict[str, Any]] = list(validated.get("validationWarnings", []))
    coverage_summary = _validated_coverage_summary(coverage)
    coverage_note = _coverage_note(coverage_summary)

    if workflow == "chapters":
        results = validated["chapters"]
        if sequence_start_ms != 0:
            warnings.append(
                {
                    "reasonCode": "NONZERO_SEQUENCE_START_IGNORED_FOR_ELAPSED_CHAPTER_TIME",
                    "capturedSequenceStartMs": sequence_start_ms,
                }
            )
        for index, result in enumerate(results):
            unit = units[unit_positions[result["startUnitId"]]]
            original_word_id = str(unit["startWordId"])
            word_id = refinements.get(result["chapterId"], {}).get("startWordId", original_word_id)
            word = words[word_positions[word_id]]
            mapped_start_ms = _milliseconds(word.get("startMs"), f"{word_id}.startMs")
            start_ms = 0 if index == 0 else mapped_start_ms
            valid_range = (
                0 <= start_ms < sequence_end_ms
                if index == 0
                else sequence_start_ms <= start_ms < sequence_end_ms
            )
            if not valid_range:
                raise ValueError(f"Chapter {result['chapterId']} maps outside the sequence range.")
            anchor_note = ""
            if index == 0 and mapped_start_ms > 0:
                warnings.append(
                    {
                        "reasonCode": "FIRST_CHAPTER_ANCHORED_BEFORE_TRANSCRIPT_SPEECH",
                        "chapterId": result["chapterId"],
                        "mappedSpeechStartMs": mapped_start_ms,
                    }
                )
                anchor_note = (
                    "First chapter anchored to elapsed sequence time 0 before mapped speech; "
                    "a silent or visual intro could not be interpreted from transcripts."
                )
            marker_id = f"chapter_{index + 1:04d}"
            ownership = f"automation-agent:youtube-chapters:v1:{run_id}:{result['chapterId']}"
            comments = _marker_comment(
                [
                    f"Reason: {result['reason']}",
                    f"Uncertainty: {result['uncertainty']}" if result["uncertainty"] else "",
                    anchor_note,
                    coverage_note,
                    "Transcript-based proposal; visually driven or silent content was not analyzed.",
                    ownership,
                ]
            )
            markers.append(
                {
                    "markerId": marker_id,
                    "resultId": result["chapterId"],
                    "name": result["title"],
                    "type": "COMMENT",
                    "startMs": start_ms,
                    "durationMs": 0,
                    "startSeconds": float(_seconds_text(start_ms)),
                    "durationSeconds": 0.0,
                    "comments": comments,
                }
            )
        for previous, current in zip(markers, markers[1:]):
            gap_ms = current["startMs"] - previous["startMs"]
            if gap_ms <= 0:
                raise ValueError("Refined chapter boundaries are not strictly ascending.")
            if gap_ms < YOUTUBE_MIN_SECTION_MS:
                warnings.append(
                    {
                        "reasonCode": "SHORT_CHAPTER_SECTION",
                        "previousResultId": previous["resultId"],
                        "nextResultId": current["resultId"],
                        "durationMs": gap_ms,
                    }
                )
        if len(markers) < YOUTUBE_MIN_CHAPTERS:
            warnings.append(
                {
                    "reasonCode": "FEWER_THAN_PLATFORM_PROFILE_CHAPTERS",
                    "actual": len(markers),
                    "profileMinimum": YOUTUBE_MIN_CHAPTERS,
                }
            )
        chapter_text = format_chapter_lines(
            {"startMs": marker["startMs"], "title": marker["name"]}
            for marker in markers
        )
    elif workflow == "highlights":
        item_bounds: dict[str, tuple[int, int]] = {}
        for word in words:
            item_id = str(word["itemId"])
            start_ms = _milliseconds(word.get("startMs"), f"{word['wordId']}.startMs")
            end_ms = _milliseconds(word.get("endMs"), f"{word['wordId']}.endMs")
            old = item_bounds.get(item_id)
            item_bounds[item_id] = (
                min(old[0], start_ms) if old else start_ms,
                max(old[1], end_ms) if old else end_ms,
            )
        for index, result in enumerate(validated["candidates"]):
            start_unit = units[unit_positions[result["startUnitId"]]]
            end_unit = units[unit_positions[result["endUnitId"]]]
            original_start = str(start_unit["startWordId"])
            original_end = str(end_unit["endWordId"])
            refinement = refinements.get(result["candidateId"], {})
            start_word_id = refinement.get("startWordId", original_start)
            end_word_id = refinement.get("endWordId", original_end)
            if word_positions[start_word_id] > word_positions[end_word_id]:
                raise ValueError(f"Refined candidate {result['candidateId']} has a reversed word range.")
            start_word = words[word_positions[start_word_id]]
            end_word = words[word_positions[end_word_id]]
            speech_start_ms = _milliseconds(start_word.get("startMs"), f"{start_word_id}.startMs")
            speech_end_ms = _milliseconds(end_word.get("endMs"), f"{end_word_id}.endMs")
            first_bounds = item_bounds[str(start_word["itemId"])]
            last_bounds = item_bounds[str(end_word["itemId"])]
            start_ms = max(sequence_start_ms, first_bounds[0], speech_start_ms - HIGHLIGHT_HANDLE_BEFORE_MS)
            end_ms = min(sequence_end_ms, last_bounds[1], speech_end_ms + HIGHLIGHT_HANDLE_AFTER_MS)
            if end_ms <= start_ms:
                raise ValueError(f"Candidate {result['candidateId']} maps to a non-positive duration.")
            marker_id = f"highlight_{index + 1:04d}"
            name = f"{result['score']} {result['type']} - {result['title']}"
            ownership = f"automation-agent:highlights-teasers:v1:{run_id}:{result['candidateId']}"
            comments = _marker_comment(
                [
                    f"Score: {result['score']}",
                    f"Type: {result['type']}",
                    f"Intended use: {result['intendedUse']}",
                    f"Representative quote or beat: {result['representativeQuote']}",
                    f"Reason: {result['reason']}",
                    f"Uncertainty: {result['uncertainty']}" if result["uncertainty"] else "",
                    f"Handle note: {result['handleNote']}" if result["handleNote"] else "",
                    coverage_note,
                    "Transcript-based proposal; visually driven or silent content was not analyzed.",
                    ownership,
                ]
            )
            markers.append(
                {
                    "markerId": marker_id,
                    "resultId": result["candidateId"],
                    "name": name,
                    "type": "COMMENT",
                    "startMs": start_ms,
                    "durationMs": end_ms - start_ms,
                    "startSeconds": float(_seconds_text(start_ms)),
                    "durationSeconds": float(_seconds_text(end_ms - start_ms)),
                    "comments": comments,
                    "score": result["score"],
                    "candidateType": result["type"],
                }
            )
        chapter_text = ""
    else:
        raise ValueError("workflow must be 'chapters' or 'highlights'.")

    return {
        "contractVersion": CONTRACT_VERSION,
        "workflow": workflow,
        "runId": run_id,
        "markers": markers,
        "chapterText": chapter_text,
        "compatibilityWarnings": warnings,
        "coverageSummary": coverage_summary,
        "rejectedCandidates": list(validated.get("rejectedCandidates", [])),
    }


def _tsv_value(field: str, marker: dict[str, Any]) -> str:
    if field == "startSeconds":
        return _seconds_text(int(marker["startMs"]))
    if field == "durationSeconds":
        return _seconds_text(int(marker["durationMs"]))
    return str(marker[field])


def write_materialized_outputs(output: Path, plan: dict[str, Any]) -> None:
    output.mkdir(parents=True, exist_ok=True)
    (output / "marker-plan.json").write_text(
        json.dumps(plan, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    marker_path = output / "marker-plan.tsv"
    with marker_path.open("w", encoding="utf-8", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=MARKER_COLUMNS, dialect="excel-tab", extrasaction="ignore")
        writer.writeheader()
        writer.writerows(
            {field: _tsv_value(field, marker) for field in MARKER_COLUMNS}
            for marker in plan["markers"]
        )
    summary = {
        "workflow": plan["workflow"],
        "runId": plan["runId"],
        "markerCount": len(plan["markers"]),
        "coverageStatus": plan["coverageSummary"]["status"],
        "compatibilityWarningCount": len(plan["compatibilityWarnings"]),
    }
    with (output / "summary.tsv").open("w", encoding="utf-8", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=SUMMARY_COLUMNS, dialect="excel-tab")
        writer.writeheader()
        writer.writerow(summary)
    if plan["workflow"] == "chapters":
        (output / "chapter-text.txt").write_text(plan["chapterText"], encoding="utf-8")
    (output / "editor-report.md").write_text(editor_report_markdown(plan), encoding="utf-8")
    verify_marker_tsv(marker_path, plan)
    verify_summary_tsv(output / "summary.tsv", plan)


def verify_marker_tsv(path: Path, plan: dict[str, Any]) -> None:
    with path.open(encoding="utf-8", newline="") as handle:
        reader = csv.DictReader(handle, dialect="excel-tab")
        rows = list(reader)
    if tuple(reader.fieldnames or ()) != MARKER_COLUMNS:
        raise ValueError("marker-plan.tsv header does not match its frozen contract.")
    expected = [
        {field: _tsv_value(field, marker) for field in MARKER_COLUMNS}
        for marker in plan["markers"]
    ]
    if rows != expected:
        raise ValueError("marker-plan.tsv does not match marker-plan.json.")


def verify_summary_tsv(path: Path, plan: dict[str, Any]) -> None:
    with path.open(encoding="utf-8", newline="") as handle:
        reader = csv.DictReader(handle, dialect="excel-tab")
        rows = list(reader)
    if tuple(reader.fieldnames or ()) != SUMMARY_COLUMNS:
        raise ValueError("summary.tsv header does not match its frozen contract.")
    expected = [
        {
            "workflow": str(plan["workflow"]),
            "runId": str(plan["runId"]),
            "markerCount": str(len(plan["markers"])),
            "coverageStatus": str(plan["coverageSummary"]["status"]),
            "compatibilityWarningCount": str(len(plan["compatibilityWarnings"])),
        }
    ]
    if rows != expected:
        raise ValueError("summary.tsv does not match marker-plan.json.")


def editor_report_markdown(plan: dict[str, Any]) -> str:
    lines = [
        "# Transcript-based marker proposal",
        "",
        f"- Workflow: `{plan['workflow']}`",
        f"- Proposed markers: `{len(plan['markers'])}`",
        f"- Transcript coverage status: `{plan['coverageSummary']['status']}`",
        f"- Excluded or unsupported items: `{plan['coverageSummary']['excludedItemCount']}`",
        "- Visual and silent-only content was not analyzed.",
        "",
        "## Proposed markers",
        "",
    ]
    for marker in plan["markers"]:
        lines.append(
            f"- `{_seconds_text(marker['startMs'])}s` — **{marker['name']}**"
            + (f" (`{_seconds_text(marker['durationMs'])}s`)" if marker["durationMs"] else "")
        )
    if plan.get("rejectedCandidates"):
        lines.extend(["", "## Omitted candidates", ""])
        for rejected in plan["rejectedCandidates"]:
            lines.append(
                f"- `{rejected['candidateId']}` — {rejected['title'] or '(empty title)'}: "
                f"{rejected['reason']}"
            )
    if plan["compatibilityWarnings"]:
        lines.extend(["", "## Review warnings", ""])
        for warning in plan["compatibilityWarnings"]:
            reason_code = warning["reasonCode"]
            chapter_end_messages = {
                "CHAPTER_END_UNIT_IGNORED_INVALID_ID": (
                    "The suggested chapter end was not a valid transcript unit ID and was ignored."
                ),
                "CHAPTER_END_UNIT_IGNORED_UNKNOWN_ID": (
                    "The suggested chapter end was not present in this transcript and was ignored."
                ),
                "CHAPTER_END_UNIT_IGNORED_BEFORE_START": (
                    "The suggested chapter end preceded its start and was ignored."
                ),
            }
            chapter_suffix = (
                f" — chapter `{warning['chapterId']}`"
                if warning.get("chapterId")
                else ""
            )
            explanation = chapter_end_messages.get(reason_code)
            explanation_suffix = f": {explanation}" if explanation else ""
            lines.append(f"- `{reason_code}`{chapter_suffix}{explanation_suffix}")
    lines.extend(
        [
            "",
            "Review every title, boundary, score, duration, overlap, and coverage warning before publishing.",
        ]
    )
    return "\n".join(lines).rstrip() + "\n"


def _read_inventory(path: Path) -> list[dict[str, Any]]:
    with path.open(encoding="utf-8", newline="") as handle:
        reader = csv.DictReader(handle, dialect="excel-tab")
        rows = list(reader)
    if tuple(reader.fieldnames or ()) != INVENTORY_COLUMNS:
        raise ValueError("Marker inventory header does not match its frozen contract.")
    result: list[dict[str, Any]] = []
    for row_number, row in enumerate(rows, start=2):
        try:
            index = int(row["index"])
            start = float(row["startSeconds"])
            duration = float(row["durationSeconds"])
        except (TypeError, ValueError) as error:
            raise ValueError(f"Marker inventory row {row_number} has invalid numeric fields.") from error
        if index < 0 or not math.isfinite(start) or not math.isfinite(duration) or duration < 0:
            raise ValueError(f"Marker inventory row {row_number} has an invalid marker range.")
        result.append({**row, "index": index, "startSeconds": start, "durationSeconds": duration})
    return result


def _ownership(row: dict[str, Any], workflow: str) -> tuple[str, str] | None:
    namespace = "youtube-chapters" if workflow == "chapters" else "highlights-teasers"
    lines = [line.strip() for line in str(row["comments"]).splitlines() if line.strip()]
    if not lines:
        return None
    ownership_pattern = (
        rf"automation-agent:{re.escape(namespace)}:v1:([A-Za-z0-9][A-Za-z0-9._-]{{0,79}}):"
        rf"([A-Za-z0-9][A-Za-z0-9._-]{{0,79}})"
    )
    match = re.fullmatch(ownership_pattern, lines[-1])
    return (match.group(1), match.group(2)) if match else None


def _marker_signature(row: dict[str, Any]) -> tuple[Any, ...]:
    return (
        row["name"], row["type"], round(row["startSeconds"], 3),
        round(row["durationSeconds"], 3), row["comments"],
    )


def _marker_type_matches(read_back: Any, planned: Any) -> bool:
    # Premiere writes the API enum token (for example COMMENT) but reads the
    # same marker type back using display casing (for example Comment).
    return str(read_back).strip().casefold() == str(planned).strip().casefold()


def snapshot_marker_inventory(path: Path, *, workflow: str) -> dict[str, Any]:
    rows = _read_inventory(path)
    return {
        "workflow": workflow,
        "rows": rows,
        "unrelatedSignatures": [list(_marker_signature(row)) for row in rows if _ownership(row, workflow) is None],
    }


def verify_created_marker_inventory(
    baseline: dict[str, Any], current_path: Path, plan: dict[str, Any], *, output_dir: Path,
) -> dict[str, Any]:
    workflow = str(plan["workflow"])
    run_id = str(plan["runId"])
    current = _read_inventory(current_path)
    by_result: dict[str, dict[str, Any]] = {}
    for row in current:
        owned = _ownership(row, workflow)
        if owned and owned[0] == run_id:
            if owned[1] in by_result:
                raise ValueError(f"New marker run contains duplicate result ID {owned[1]}.")
            by_result[owned[1]] = row
    expected_ids = {str(marker["resultId"]) for marker in plan["markers"]}
    if set(by_result) != expected_ids:
        raise ValueError("New marker read-back does not contain exactly the validated result IDs.")
    for marker in plan["markers"]:
        row = by_result[str(marker["resultId"])]
        if row["name"] != marker["name"] or not _marker_type_matches(row["type"], marker["type"]) or row["comments"] != marker["comments"]:
            raise ValueError(f"New marker {marker['resultId']} text/type read-back differs from its validated plan.")
        if abs(row["startSeconds"] - float(marker["startSeconds"])) > 0.001:
            raise ValueError(f"New marker {marker['resultId']} start read-back differs from its validated plan.")
        if abs(row["durationSeconds"] - float(marker["durationSeconds"])) > 0.001:
            raise ValueError(f"New marker {marker['resultId']} duration read-back differs from its validated plan.")
    baseline_unrelated = Counter(tuple(value) for value in baseline["unrelatedSignatures"])
    current_unrelated = Counter(_marker_signature(row) for row in current if _ownership(row, workflow) is None)
    if baseline_unrelated != current_unrelated:
        raise ValueError("An unrelated sequence marker changed during new-run creation.")
    old = [row for row in current if (owned := _ownership(row, workflow)) and owned[0] != run_id]
    old.sort(key=lambda row: row["index"], reverse=True)
    output_dir.mkdir(parents=True, exist_ok=True)
    with (output_dir / "retire-markers.tsv").open("w", encoding="utf-8", newline="") as handle:
        writer = csv.writer(handle, dialect="excel-tab")
        writer.writerow(("markerRef", "index"))
        writer.writerows((row["markerRef"], row["index"]) for row in old)
    return {"newMarkerCount": len(by_result), "priorOwnedMarkerCount": len(old)}


def verify_final_marker_inventory(
    baseline: dict[str, Any], current_path: Path, plan: dict[str, Any]
) -> dict[str, Any]:
    workflow = str(plan["workflow"])
    run_id = str(plan["runId"])
    current = _read_inventory(current_path)
    current_new = {
        owned[1]: row for row in current
        if (owned := _ownership(row, workflow)) and owned[0] == run_id
    }
    if len(current_new) != len(plan["markers"]):
        raise ValueError("Final marker read-back lost part of the verified new run.")
    for marker in plan["markers"]:
        row = current_new.get(str(marker["resultId"]))
        if row is None:
            raise ValueError(f"Final marker read-back lost result {marker['resultId']}.")
        if row["name"] != marker["name"] or not _marker_type_matches(row["type"], marker["type"]) or row["comments"] != marker["comments"]:
            raise ValueError(f"Final marker {marker['resultId']} text/type differs from its validated plan.")
        if abs(row["startSeconds"] - float(marker["startSeconds"])) > 0.001 or abs(row["durationSeconds"] - float(marker["durationSeconds"])) > 0.001:
            raise ValueError(f"Final marker {marker['resultId']} timing differs from its validated plan.")
    baseline_unrelated = Counter(tuple(value) for value in baseline["unrelatedSignatures"])
    current_unrelated = Counter(_marker_signature(row) for row in current if _ownership(row, workflow) is None)
    if baseline_unrelated != current_unrelated:
        raise ValueError("An unrelated sequence marker changed during prior-run retirement.")
    old_remaining = sum(
        1 for row in current
        if (owned := _ownership(row, workflow)) and owned[0] != run_id
    )
    return {"newMarkerCount": len(current_new), "priorOwnedMarkersRemaining": old_remaining}
