"""CLI interface for soniox-transcribe."""

import json
import os
from pathlib import Path
from typing import Annotated, Optional

import typer
import typer.core
from rich.console import Console
from rich.progress import Progress, SpinnerColumn, TextColumn

from .audio import is_video_file, prepare_audio
from .client import SonioxClient, SonioxError
from .models import TranscriptionContext
from .subtitle import SubtitleFormat, tokens_to_subtitle


class _DefaultToTranscribe(typer.core.TyperGroup):
    """Route to 'transcribe' when no explicit subcommand is given."""

    def parse_args(self, ctx, args):
        if args and args[0] not in self.commands:
            if not args[0].startswith("-"):
                args.insert(0, "transcribe")
            else:
                group_opts = {
                    opt for p in self.get_params(ctx) for opt in (*p.opts, *p.secondary_opts)
                }
                if args[0] not in group_opts:
                    args.insert(0, "transcribe")
        return super().parse_args(ctx, args)


app = typer.Typer(
    name="soniox-transcribe",
    help="Generate subtitles from audio/video using Soniox Speech-to-Text API",
    no_args_is_help=True,
    cls=_DefaultToTranscribe,
)
console = Console()


@app.command()
def transcribe(
    input_path: Annotated[
        Optional[Path],
        typer.Argument(help="Path to audio/video file"),
    ] = None,
    url: Annotated[
        Optional[str],
        typer.Option("--url", "-u", help="Public URL of audio file"),
    ] = None,
    output: Annotated[
        Optional[Path],
        typer.Option("--output", "-o", help="Output file path (default: input with new ext)"),
    ] = None,
    format: Annotated[
        Optional[str],
        typer.Option("--format", "-f", help="Output format: srt, vtt, txt (default: srt)"),
    ] = None,
    language: Annotated[
        Optional[list[str]],
        typer.Option("--language", "-l", help="Language hints (default: zh, en)"),
    ] = None,
    no_speaker: Annotated[
        bool,
        typer.Option("--no-speaker", help="Disable speaker diarization and labels"),
    ] = False,
    max_chars: Annotated[
        int,
        typer.Option("--max-chars", help="Max characters per subtitle line"),
    ] = 42,
    model: Annotated[
        Optional[str],
        typer.Option("--model", "-m", help="Soniox model to use (default: stt-async-v4)"),
    ] = None,
    context_terms: Annotated[
        Optional[list[str]],
        typer.Option("--context-term", "-t", help="Custom terms/vocabulary (can be repeated)"),
    ] = None,
    context_text: Annotated[
        Optional[str],
        typer.Option("--context-text", help="Background text to improve transcription accuracy"),
    ] = None,
    context_text_file: Annotated[
        Optional[Path],
        typer.Option("--context-text-file", help="File containing background text"),
    ] = None,
    context_json: Annotated[
        Optional[str],
        typer.Option("--context", "-c", help="JSON string with context configuration"),
    ] = None,
    context_file: Annotated[
        Optional[Path],
        typer.Option("--context-file", help="JSON file with full context configuration"),
    ] = None,
):
    """
    Transcribe audio/video and generate subtitles.

    Examples:

        soniox-transcribe video.mp4

        soniox-transcribe video.mp4 -o video.vtt

        soniox-transcribe --url https://example.com/audio.mp3

        soniox-transcribe interview.wav --no-speaker
    """
    if not input_path and not url:
        console.print("[red]Error:[/red] Provide either a file path or --url")
        raise typer.Exit(1)

    if input_path and url:
        console.print("[red]Error:[/red] Provide either a file path or --url, not both")
        raise typer.Exit(1)

    if input_path and not input_path.exists():
        console.print(f"[red]Error:[/red] File not found: {input_path}")
        raise typer.Exit(1)

    # Determine output format
    subtitle_format = SubtitleFormat.SRT
    if format:
        fmt_lower = format.lower()
        if fmt_lower == "vtt":
            subtitle_format = SubtitleFormat.VTT
        elif fmt_lower == "txt":
            subtitle_format = SubtitleFormat.TXT
        elif fmt_lower != "srt":
            console.print(f"[red]Error:[/red] Unknown format: {format}. Use 'srt', 'vtt', or 'txt'")
            raise typer.Exit(1)
    elif output:
        suffix = output.suffix.lower()
        if suffix == ".vtt":
            subtitle_format = SubtitleFormat.VTT
        elif suffix == ".txt":
            subtitle_format = SubtitleFormat.TXT

    # Determine output path
    output_path = output
    if not output_path and input_path:
        ext_map = {
            SubtitleFormat.SRT: ".srt",
            SubtitleFormat.VTT: ".vtt",
            SubtitleFormat.TXT: ".txt",
        }
        output_path = input_path.with_suffix(ext_map[subtitle_format])

    # Default language hints
    lang_hints = language if language else ["zh", "en"]

    # Speaker diarization and labels (enabled by default)
    enable_diarization = not no_speaker
    include_speaker_label = not no_speaker

    # Build context (priority: --context > --context-file > --context-text/--context-term)
    context: TranscriptionContext | dict | None = None
    if context_json:
        try:
            context = json.loads(context_json)
        except json.JSONDecodeError as e:
            console.print(f"[red]Error:[/red] Invalid JSON in --context: {e}")
            raise typer.Exit(1)
    elif context_file:
        if not context_file.exists():
            console.print(f"[red]Error:[/red] Context file not found: {context_file}")
            raise typer.Exit(1)
        try:
            context = json.loads(context_file.read_text(encoding="utf-8"))
        except json.JSONDecodeError as e:
            console.print(f"[red]Error:[/red] Invalid JSON in context file: {e}")
            raise typer.Exit(1)
    elif context_text or context_text_file or context_terms:
        # Resolve text content (--context-text takes priority over --context-text-file)
        text_content = context_text
        if not text_content and context_text_file:
            if not context_text_file.exists():
                console.print(f"[red]Error:[/red] Context text file not found: {context_text_file}")
                raise typer.Exit(1)
            text_content = context_text_file.read_text(encoding="utf-8").strip()
        context = TranscriptionContext(
            text=text_content,
            terms=context_terms,
        )

    try:
        client = SonioxClient()
    except SonioxError as e:
        console.print(f"[red]Error:[/red] {e}")
        raise typer.Exit(1)

    with client:
        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            console=console,
        ) as progress:
            task = progress.add_task("Transcribing...", total=None)

            def on_progress(status: str, attempt: int):
                progress.update(task, description=f"Status: {status} (poll #{attempt})")

            audio_path = input_path
            temp_audio = False

            try:
                if input_path and is_video_file(input_path):
                    progress.update(task, description="Extracting audio from video...")
                    audio_path, temp_audio = prepare_audio(input_path)

                if audio_path:
                    progress.update(task, description="Uploading file...")

                result = client.transcribe(
                    file_path=audio_path,
                    audio_url=url,
                    model=model,
                    language_hints=lang_hints,
                    enable_speaker_diarization=enable_diarization,
                    context=context,
                    on_progress=on_progress,
                )
            except SonioxError as e:
                console.print(f"[red]Error:[/red] {e}")
                raise typer.Exit(1)
            finally:
                if temp_audio and audio_path:
                    os.unlink(audio_path)

        subtitle_content = tokens_to_subtitle(
            result.tokens,
            format=subtitle_format,
            max_chars_per_line=max_chars,
            include_speaker=include_speaker_label,
        )

        if output_path:
            output_path.write_text(subtitle_content, encoding="utf-8")
            console.print(f"[green]Saved:[/green] {output_path}")
        else:
            console.print(subtitle_content)


if __name__ == "__main__":
    app()
