"""
Module: src.ui.streamlit_app
Workorder: WO02
Purpose: Streamlit interface with optional diarization and Ollama post-processing.

Dependencies:
    - streamlit: UI rendering
    - pathlib/tempfile: Temporary file storage

Inputs:
    - Uploaded audio files (`.wav`, `.mp3`, `.m4a`)

Outputs:
    - Triggered processing and transcript preview

Key Functions/Classes:
    - render_ui: Renders Streamlit UI

Tests: tests/unit/test_placeholder.py
"""
from pathlib import Path
import platform
import re
import subprocess
from tempfile import TemporaryDirectory
import time
from uuid import uuid4

from src.app.main import (
    get_ollama_service_runtime_status,
    run_ollama_postprocessing,
    run_transcription_session,
)
from src.shared.errors import AudioTranskriberError, ExternalServiceError

DEFAULT_OLLAMA_MODEL = "auto"
DEFAULT_OLLAMA_HOST = "http://localhost:11434"
ALLOWED_UPLOAD_EXTENSIONS = {".wav", ".mp3", ".m4a"}


def _safe_uploaded_filename(original_name: str) -> str:
    """Build a safe temporary filename for uploaded audio.

    Args:
        original_name: Raw filename from upload metadata.

    Returns:
        Sanitized filename that stays within temp directory boundaries.

    Raises:
        ValueError: If filename is empty or extension is not supported.
    """
    leaf_name = Path(str(original_name)).name
    if not leaf_name.strip():
        raise ValueError("Leerer Dateiname ist nicht erlaubt.")

    suffix = Path(leaf_name).suffix.lower()
    if suffix not in ALLOWED_UPLOAD_EXTENSIONS:
        allowed = ", ".join(sorted(ALLOWED_UPLOAD_EXTENSIONS))
        raise ValueError(f"Ungültige Dateiendung '{suffix}'. Erlaubt: {allowed}")

    base_name = Path(leaf_name).stem
    normalized_base = re.sub(r"[^A-Za-z0-9._-]+", "_", base_name).strip("._")
    if not normalized_base:
        normalized_base = "upload"

    return f"{normalized_base}_{uuid4().hex[:8]}{suffix}"


def _build_ollama_status_message(status: dict[str, object]) -> tuple[str, str]:
    """Return message type and text for Ollama service status.

    Args:
        status: Runtime status dictionary.

    Returns:
        Tuple of (`success`/`warning`, message).
    """
    reachable = bool(status.get("reachable", False))
    models = status.get("models", [])
    message = str(status.get("message", "")).strip()

    if not reachable:
        return ("warning", message or "Ollama ist aktuell nicht erreichbar.")

    if not isinstance(models, list) or not models:
        return (
            "warning",
            message or "Ollama läuft, aber es sind keine Modelle installiert.",
        )

    selected_model = str(status.get("selected_model", "")).strip()
    model_info = f"Aktives Modell: {selected_model}." if selected_model else ""
    return ("success", (message + " " + model_info).strip())


def _wait_for_ollama_ready(host: str, attempts: int = 8, delay_seconds: float = 1.0) -> bool:
    """Wait briefly until Ollama service becomes reachable.

    Args:
        host: Ollama host URL.
        attempts: Number of availability checks.
        delay_seconds: Delay between checks.

    Returns:
        True if service becomes available within attempts.
    """
    for _ in range(attempts):
        status = get_ollama_service_runtime_status(model=DEFAULT_OLLAMA_MODEL, host=host)
        if bool(status.get("reachable", False)):
            return True
        time.sleep(delay_seconds)
    return False


def render_ui() -> None:
    """Render WO01 Streamlit UI.

    Raises:
        RuntimeError: If Streamlit is not installed.
    """
    try:
        import streamlit as st  # type: ignore[import-not-found]
    except ModuleNotFoundError as error:
        raise RuntimeError(
            "Streamlit is not installed. Install it to run the UI: pip install streamlit"
        ) from error

    st.set_page_config(page_title="Audio-Transkriber", layout="centered")
    st.title("Audio-Transkriber")
    st.caption("Lokale Transkription mit WhisperX (MVP)")

    uploaded_file = st.file_uploader(
        "Audio-Datei auswählen",
        type=["wav", "mp3", "m4a"],
        accept_multiple_files=False,
    )

    language_label = st.radio(
        "Sprache",
        options=["Auto-detect", "Deutsch", "Englisch"],
        horizontal=True,
    )
    language_mode_map = {
        "Auto-detect": "auto",
        "Deutsch": "de",
        "Englisch": "en",
    }
    language_mode = language_mode_map[language_label]

    process_clicked = st.button("Transkription starten", use_container_width=True)
    diarization_enabled = st.checkbox("Diarization aktivieren", value=False)

    if process_clicked and uploaded_file is None:
        st.warning("Bitte zuerst eine Audio-Datei auswählen.")

    if process_clicked and uploaded_file is not None:
        progress_bar = st.progress(0, text="Initialisierung")

        def _progress_callback(value: float) -> None:
            bounded = min(max(value, 0.0), 1.0)
            progress_bar.progress(int(bounded * 100), text="Transkription läuft")

        try:
            with TemporaryDirectory() as temp_dir:
                safe_name = _safe_uploaded_filename(uploaded_file.name)
                temp_path = Path(temp_dir) / safe_name
                temp_path.write_bytes(uploaded_file.getbuffer())

                result = run_transcription_session(
                    source_audio_path=temp_path,
                    language_mode=language_mode,
                    diarization_enabled=diarization_enabled,
                    progress_callback=_progress_callback,
                )

                st.session_state["last_transcript_text"] = result["transcript_text"]
                st.session_state["last_export_json"] = result["exports"]["json"]
                st.session_state["last_export_markdown"] = result["exports"]["markdown"]
                st.session_state["last_export_text"] = result["exports"]["text"]
                st.session_state["last_output_dir"] = result["output_dir"]

            progress_bar.progress(100, text="Fertig")
            st.success("Transkription erfolgreich abgeschlossen.")
            st.subheader("Vorschau")
            st.text_area("Transkript", value=result["transcript_text"], height=280)
            st.caption(
                "Exporte erstellt: "
                f"JSON={result['exports']['json']}, "
                f"MD={result['exports']['markdown']}, "
                f"TXT={result['exports']['text']}"
            )
        except AudioTranskriberError as error:
            st.error(f"Verarbeitung fehlgeschlagen: {error}")
        except Exception as error:  # pragma: no cover - safety net
            st.error(f"Unerwarteter Fehler: {error}")

    st.divider()
    st.subheader("Ollama Post-Processing")

    ollama_model = DEFAULT_OLLAMA_MODEL
    ollama_host = DEFAULT_OLLAMA_HOST

    if st.button("Ollama-Status aktualisieren", use_container_width=True):
        st.session_state["ollama_status"] = get_ollama_service_runtime_status(
            model=ollama_model,
            host=ollama_host,
        )

    if "ollama_status" not in st.session_state:
        st.session_state["ollama_status"] = get_ollama_service_runtime_status(
            model=ollama_model,
            host=ollama_host,
        )

    ollama_status = st.session_state.get("ollama_status", {})
    status_type, status_message = _build_ollama_status_message(ollama_status)
    if status_type == "success":
        st.success(status_message)
    else:
        st.warning(status_message)

    if platform.system() == "Darwin":
        if st.button("Ollama starten", use_container_width=True):
            try:
                script_path = (
                    Path(__file__).resolve().parents[2] / "scripts" / "start_ollama.command"
                )
                subprocess.run([str(script_path)], check=False)
                ready = _wait_for_ollama_ready(ollama_host)
                st.session_state["ollama_status"] = get_ollama_service_runtime_status(
                    model=ollama_model,
                    host=ollama_host,
                )
                if ready:
                    st.success("Ollama wurde gestartet.")
                else:
                    st.info(
                        "Start ausgelöst, aber Ollama ist noch nicht bereit. "
                        "Bitte in wenigen Sekunden erneut versuchen."
                    )
            except Exception as error:  # pragma: no cover - safety net
                st.error(f"Konnte Ollama nicht starten: {error}")

    can_run_ollama = bool(st.session_state.get("last_transcript_text"))
    if st.button("Zusammenfassung & Action Items erstellen", disabled=not can_run_ollama):
        try:
            if not _wait_for_ollama_ready(ollama_host, attempts=3, delay_seconds=0.5):
                st.session_state["ollama_status"] = get_ollama_service_runtime_status(
                    model=ollama_model,
                    host=ollama_host,
                )
                st.error(
                    "Ollama ist noch nicht bereit. Bitte zuerst "
                    "'Ollama starten' klicken oder kurz warten und erneut versuchen."
                )
                return

            transcript_text = st.session_state.get("last_transcript_text", "")
            export_json_path = st.session_state.get("last_export_json")
            export_markdown_path = st.session_state.get("last_export_markdown")
            export_text_path = st.session_state.get("last_export_text")
            ollama_output = run_ollama_postprocessing(
                transcript_text=transcript_text,
                model=ollama_model,
                host=ollama_host,
                export_json_path=Path(export_json_path) if export_json_path else None,
                export_markdown_path=(
                    Path(export_markdown_path) if export_markdown_path else None
                ),
                export_text_path=Path(export_text_path) if export_text_path else None,
            )
            st.text_area("Ollama Ausgabe", value=ollama_output, height=220)
            st.session_state["ollama_status"] = get_ollama_service_runtime_status(
                model=ollama_model,
                host=ollama_host,
            )
        except ExternalServiceError as error:
            st.error(f"Ollama nicht verfügbar: {error}")
            st.session_state["ollama_status"] = get_ollama_service_runtime_status(
                model=ollama_model,
                host=ollama_host,
            )
        except AudioTranskriberError as error:
            st.error(f"Post-Processing fehlgeschlagen: {error}")
        except Exception as error:  # pragma: no cover - safety net
            st.error(f"Unerwarteter Fehler: {error}")

    output_dir = st.session_state.get("last_output_dir")
    if output_dir:
        st.divider()
        output_dir_path = Path(str(output_dir)).expanduser()
        if st.button("Ausgabeverzeichnis öffnen", use_container_width=True):
            try:
                if not output_dir_path.exists():
                    st.error(f"Ausgabeverzeichnis nicht gefunden: {output_dir_path}")
                else:
                    subprocess.run(["open", str(output_dir_path)], check=True)
                    st.success(f"Ausgabeverzeichnis geöffnet: {output_dir_path}")
            except Exception as error:  # pragma: no cover - safety net
                st.error(f"Konnte Ausgabeverzeichnis nicht öffnen: {error}")


if __name__ == "__main__":
    render_ui()
