#!/usr/bin/env python3
"""Helper utilities for transcribe.sh - replaces inline Python snippets."""

from __future__ import annotations

import argparse
import array
import importlib
import logging
import math
import os
import sys
from typing import TYPE_CHECKING
import wave

if TYPE_CHECKING:
    import types

logger = logging.getLogger(__name__)


def _try_import(name: str) -> types.ModuleType | None:
    """Attempt to import a module, returning None on failure."""
    try:
        return importlib.import_module(name)
    except ImportError:
        return None


def get_python_version() -> str:
    """Return Python major.minor version string."""
    return f"{sys.version_info.major}.{sys.version_info.minor}"


def check_faster_whisper() -> bool:
    """Check if faster_whisper is importable. Exit 7 if not."""
    return _try_import("faster_whisper") is not None


def check_diarization_deps() -> bool:
    """Check if diarization dependencies are available.

    Returns False with warning if missing.
    """
    _sf = _try_import("soundfile")
    _sb = _try_import("speechbrain")
    _torch = _try_import("torch")
    if _sf is None or _sb is None or _torch is None:
        logger.warning(
            "Diarization deps missing offline; speaker labels will be skipped.",
        )
        return False
    return True


def check_ctranslate2() -> bool:
    """Check if ctranslate2 is importable."""
    return _try_import("ctranslate2") is not None


def print_deps_installed() -> None:
    """Print confirmation that Python dependencies are installed."""
    logger.info("Python %s dependencies installed.", sys.version.split()[0])


def generate_sine_wav(
    outfile: str,
    frequency: float = 1000.0,
    duration: int = 3,
    sample_rate: int = 16000,
    amplitude: float = 0.3,
) -> bool:
    """Generate a sine wave WAV file using only Python stdlib.

    Args:
        outfile: Output WAV file path
        frequency: Tone frequency in Hz (default: 1000)
        duration: Duration in seconds (default: 3)
        sample_rate: Sample rate in Hz (default: 16000)
        amplitude: Amplitude 0.0-1.0 (default: 0.3)

    Returns:
        True on success, False on failure
    """
    try:
        n_samples = sample_rate * duration
        data = array.array(
            "h",
            [
                int(
                    max(
                        -1.0,
                        min(
                            1.0,
                            amplitude
                            * math.sin(2 * math.pi * frequency * (i / sample_rate)),
                        ),
                    )
                    * 32767
                )
                for i in range(n_samples)
            ],
        )
        with wave.open(outfile, "w") as wf:
            wf.setnchannels(1)
            wf.setsampwidth(2)
            wf.setframerate(sample_rate)
            wf.writeframes(data.tobytes())
    except OSError:
        logger.exception("Failed to generate WAV")
        return False
    else:
        return True


def prepare_model(model_name: str, model_dir: str) -> bool:
    """Download a whisper model for offline use.

    Args:
        model_name: Model name (tiny, base, small, medium, large-v3, etc.)
        model_dir: Directory to store the model

    Returns:
        True on success, False on failure
    """
    fw = _try_import("faster_whisper")
    if fw is None:
        logger.error("faster_whisper is not installed")
        return False

    try:
        hf_logging = _try_import("huggingface_hub.logging")
        if hf_logging is not None:
            hf_logging.set_verbosity_info()
        hh = _try_import("huggingface_hub")
        if hh is not None:
            hh.constants.HF_HUB_DISABLE_PROGRESS_BARS = False
            os.environ["HF_HUB_DISABLE_PROGRESS_BARS"] = "0"

        logger.info("Preparing model '%s' into %s", model_name, model_dir)
        logger.info(
            "Downloading model files (progress bar should appear below)...",
        )
        fw.WhisperModel(
            model_name,
            device="cpu",
            compute_type="int8",
            download_root=model_dir,
        )
        logger.info("Model prepared.")
    except (OSError, RuntimeError):
        logger.exception("Failed to prepare model")
        return False
    else:
        return True


def test_cuda() -> bool:
    """Test CUDA initialization with faster-whisper.

    Returns:
        True if CUDA works, False otherwise
    """
    fw = _try_import("faster_whisper")
    if fw is None:
        logger.error("faster_whisper is not installed")
        return False

    try:
        fw.WhisperModel("tiny", device="cuda", compute_type="float16")
        logger.info("CUDA test init succeeded.")
    except (OSError, RuntimeError):
        logger.exception("CUDA test failed")
        return False
    else:
        return True


def _handle_python_version() -> None:
    """Handle python-version command."""
    logger.info("%s", get_python_version())


def _handle_check_faster_whisper() -> None:
    """Handle check-faster-whisper command."""
    if not check_faster_whisper():
        logger.error(
            "Python dependency 'faster_whisper' not found in "
            "offline mode. Run with --online to install.",
        )
        sys.exit(7)


def _handle_check_diarization() -> None:
    """Handle check-diarization command."""
    check_diarization_deps()


def _handle_check_ctranslate2() -> None:
    """Handle check-ctranslate2 command."""
    if not check_ctranslate2():
        sys.exit(1)


def _handle_deps_installed() -> None:
    """Handle deps-installed command."""
    print_deps_installed()


def _handle_generate_wav(args: argparse.Namespace) -> None:
    """Handle generate-wav command."""
    if not args.file:
        logger.error("--file is required for generate-wav")
        sys.exit(2)
    if not generate_sine_wav(args.file):
        sys.exit(1)


def _handle_prepare_model(args: argparse.Namespace) -> None:
    """Handle prepare-model command."""
    if not args.model or not args.model_dir:
        logger.error(
            "--model and --model-dir are required for prepare-model",
        )
        sys.exit(2)
    if not prepare_model(args.model, args.model_dir):
        sys.exit(1)


def _handle_test_cuda() -> None:
    """Handle test-cuda command."""
    if not test_cuda():
        sys.exit(1)


def main() -> None:
    """Parse arguments and dispatch helper commands."""
    logging.basicConfig(format="%(message)s", level=logging.INFO)

    parser = argparse.ArgumentParser(
        description="Helper utilities for transcribe.sh",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Commands:
  python-version       Print Python major.minor version
  check-faster-whisper Check if faster_whisper is installed
  check-diarization    Check diarization deps (warn if missing)
  check-ctranslate2    Check if ctranslate2 is installed
  deps-installed       Print deps installed confirmation
  generate-wav FILE    Generate a 3s 1kHz sine wave WAV
  prepare-model        Download model for offline use
  test-cuda            Test CUDA initialization
""",
    )
    parser.add_argument(
        "command",
        choices=[
            "python-version",
            "check-faster-whisper",
            "check-diarization",
            "check-ctranslate2",
            "deps-installed",
            "generate-wav",
            "prepare-model",
            "test-cuda",
        ],
        help="Command to run",
    )
    parser.add_argument("--file", help="Output file path (for generate-wav)")
    parser.add_argument("--model", help="Model name (for prepare-model)")
    parser.add_argument("--model-dir", help="Model directory (for prepare-model)")

    args = parser.parse_args()

    dispatch: dict[str, object] = {
        "python-version": _handle_python_version,
        "check-faster-whisper": _handle_check_faster_whisper,
        "check-diarization": _handle_check_diarization,
        "check-ctranslate2": _handle_check_ctranslate2,
        "deps-installed": _handle_deps_installed,
        "generate-wav": lambda: _handle_generate_wav(args),
        "prepare-model": lambda: _handle_prepare_model(args),
        "test-cuda": _handle_test_cuda,
    }

    handler = dispatch.get(args.command)
    if handler is not None and callable(handler):
        handler()


if __name__ == "__main__":
    main()
