from __future__ import annotations

import shutil
import subprocess
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Protocol

from youtube_cli_agent.errors import ToolExecutionError, ValidationError
from youtube_cli_agent.models import TranscriptResult


class TranscriptionEngine(Protocol):
    def transcribe(self, audio_path: Path, language: str | None = None) -> str:
        ...


@dataclass(slots=True)
class WhisperCliEngine:
    model: str = "tiny"
    timeout_seconds: int = 900

    def transcribe(self, audio_path: Path, language: str | None = None) -> str:
        executable = shutil.which("whisper")
        if not executable:
            raise ToolExecutionError(
                code="TRANSCRIPTION_ENGINE_MISSING",
                stage="Transcription",
                message="Whisper CLI is not installed or not in PATH.",
            )

        with tempfile.TemporaryDirectory(prefix="youtube_agent_whisper_") as temp_dir:
            cmd = [
                executable,
                str(audio_path),
                "--model",
                self.model,
                "--output_format",
                "txt",
                "--output_dir",
                temp_dir,
                "--temperature",
                "0",
                "--best_of",
                "1",
                "--beam_size",
                "1",
                "--fp16",
                "False",
            ]
            if language:
                cmd.extend(["--language", language])

            try:
                subprocess.run(
                    cmd,
                    check=True,
                    capture_output=True,
                    text=True,
                    timeout=self.timeout_seconds,
                )
            except subprocess.TimeoutExpired:
                raise ToolExecutionError(
                    code="TRANSCRIPTION_TIMEOUT",
                    stage="Transcription",
                    message="Whisper transcription timed out.",
                ) from None
            except subprocess.CalledProcessError as exc:
                stderr = (exc.stderr or "").strip()
                raise ToolExecutionError(
                    code="TRANSCRIPTION_FAILED",
                    stage="Transcription",
                    message=f"Whisper transcription failed: {stderr or 'unknown error'}",
                ) from None

            output_file = Path(temp_dir) / f"{audio_path.stem}.txt"
            if not output_file.exists():
                raise ValidationError(
                    code="TRANSCRIPTION_OUTPUT_MISSING",
                    stage="Transcription",
                    message="Whisper completed but no transcript file was produced.",
                )
            return output_file.read_text(encoding="utf-8").strip()


@dataclass(slots=True)
class TranscriptionTool:
    engine: TranscriptionEngine
    min_chars: int = 80

    def run(self, audio_path: Path, language: str | None = None) -> TranscriptResult:
        if not audio_path.exists() or audio_path.stat().st_size <= 0:
            raise ValidationError(
                code="AUDIO_INVALID",
                stage="Transcription",
                message="Audio file is missing or empty.",
            )

        text = self.engine.transcribe(audio_path=audio_path, language=language)
        normalized = " ".join(text.split())
        if len(normalized) < self.min_chars:
            raise ValidationError(
                code="TRANSCRIPT_TOO_SHORT",
                stage="Transcription",
                message="Transcript text is below minimum threshold.",
            )

        from datetime import datetime, timezone

        return TranscriptResult(
            source="Automatic Transcription",
            language=language or "unknown",
            text=normalized,
            generated_at=datetime.now(timezone.utc),
        )
