"""Explicit EdgeTTS adapter boundary for voiceover generation."""

from __future__ import annotations

from collections.abc import Callable, Sequence
from dataclasses import dataclass
from pathlib import Path
import subprocess

from autoshorts.audio.tts_plan import TTSProviderPlan, validate_tts_provider_plan
from autoshorts.ideas.candidate import ValidationResult

Runner = Callable[..., subprocess.CompletedProcess[str]]


@dataclass(frozen=True)
class EdgeTTSRequest:
    text: str
    tts_plan: TTSProviderPlan
    output_path: str
    side_effects: tuple[str, ...] = ()
    external_calls: tuple[str, ...] = ()


@dataclass(frozen=True)
class EdgeTTSResult:
    output_path: str
    provider: str
    voice: str
    returncode: int
    stdout: str
    stderr: str
    side_effects: tuple[str, ...] = ("call_edge_tts", "write_audio")
    external_calls: tuple[str, ...] = ("edge-tts",)


def _is_blank(value: str) -> bool:
    return not (value or "").strip()


def validate_edge_tts_request(request: EdgeTTSRequest) -> ValidationResult:
    errors: list[str] = []
    if _is_blank(request.text):
        errors.append("text is required")
    if _is_blank(request.output_path):
        errors.append("output_path is required")
    elif not request.output_path.endswith(".mp3"):
        errors.append("output_path must end with .mp3")
    if request.side_effects:
        errors.append("EdgeTTS requests must not have side effects")
    if request.external_calls:
        errors.append("EdgeTTS requests must not have external calls")

    plan_validation = validate_tts_provider_plan(request.tts_plan)
    errors.extend(plan_validation.errors)
    if request.tts_plan.provider != "edge_tts":
        errors.append("EdgeTTS adapter requires provider edge_tts")
    if request.tts_plan.role != "voice_generation":
        errors.append("EdgeTTS adapter requires a voice_generation plan")
    if request.tts_plan.blocks_video_gpu:
        errors.append("EdgeTTS adapter must not block the local video GPU")
    return ValidationResult(is_valid=not errors, errors=tuple(errors))


def _command(request: EdgeTTSRequest) -> list[str]:
    return [
        "edge-tts",
        "--voice",
        request.tts_plan.voice,
        "--text",
        request.text,
        "--write-media",
        request.output_path,
    ]


def synthesize_edge_tts(request: EdgeTTSRequest, *, runner: Runner = subprocess.run) -> EdgeTTSResult:
    """Call EdgeTTS to write an MP3 voiceover. This is an explicit side-effect boundary."""

    validation = validate_edge_tts_request(request)
    if not validation.is_valid:
        raise ValueError("; ".join(validation.errors))

    output_path = Path(request.output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    completed = runner(_command(request), capture_output=True, text=True, check=False)
    if completed.returncode != 0:
        raise RuntimeError(completed.stderr or "edge-tts failed")
    if not output_path.exists():
        raise RuntimeError("edge-tts completed but did not create output audio")

    return EdgeTTSResult(
        output_path=str(output_path),
        provider=request.tts_plan.provider,
        voice=request.tts_plan.voice,
        returncode=completed.returncode,
        stdout=completed.stdout,
        stderr=completed.stderr,
        side_effects=("call_edge_tts", "write_audio"),
        external_calls=("edge-tts",),
    )
