from __future__ import annotations

import os
import re
import uuid
from pathlib import Path

import streamlit as st
import yaml
from docx import Document
from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent
INPUT_DIR = BASE_DIR / "input"
OUTPUT_DIR = BASE_DIR / "output"
PROMPTS_FILE = BASE_DIR / "config" / "prompts.yaml"
ENV_FILE = BASE_DIR / ".env"

AUDIO_TYPES = ["mp3", "wav", "m4a", "aac", "ogg", "oga", "flac", "webm"]
VIDEO_TYPES = ["mp4", "mov", "m4v", "avi", "mkv", "webm"]
CONTEXT_TYPES = ["pdf", "docx", "txt", "vtt", "md"]
MEDIA_TYPES = sorted(set(AUDIO_TYPES + VIDEO_TYPES))

load_dotenv(ENV_FILE)


def load_session_types() -> dict[str, str]:
    with PROMPTS_FILE.open("r", encoding="utf-8") as file:
        config = yaml.safe_load(file)
    return {k: k.replace("_", " ").title() for k in config["sitzungsarten"].keys()}


def safe_filename(filename: str) -> str:
    name = Path(filename).name.strip() or "upload"
    stem = Path(name).stem[:80]
    suffix = Path(name).suffix.lower()[:12]
    stem = re.sub(r"[^A-Za-z0-9_. -]+", "_", stem).strip(" ._") or "upload"
    return f"{stem}_{uuid.uuid4().hex[:10]}{suffix}"


def save_upload(uploaded_file, target_dir: Path) -> Path:
    target_dir.mkdir(parents=True, exist_ok=True)
    path = target_dir / safe_filename(uploaded_file.name)
    with path.open("wb") as f:
        f.write(uploaded_file.getbuffer())
    return path


def read_text_upload(uploaded_file, label: str) -> str:
    if not uploaded_file:
        return ""

    suffix = Path(uploaded_file.name).suffix.lower()
    raw = uploaded_file.getvalue()

    if suffix in {".txt", ".vtt", ".md"}:
        text = raw.decode("utf-8", errors="ignore")
    elif suffix == ".docx":
        tmp_path = INPUT_DIR / safe_filename(uploaded_file.name)
        INPUT_DIR.mkdir(parents=True, exist_ok=True)
        tmp_path.write_bytes(raw)
        document = Document(str(tmp_path))
        text = "\n".join(p.text for p in document.paragraphs if p.text.strip())
    elif suffix == ".pdf":
        try:
            from pypdf import PdfReader  # optional dependency in Hermes/toolchain

            tmp_path = INPUT_DIR / safe_filename(uploaded_file.name)
            INPUT_DIR.mkdir(parents=True, exist_ok=True)
            tmp_path.write_bytes(raw)
            reader = PdfReader(str(tmp_path))
            text = "\n".join(page.extract_text() or "" for page in reader.pages)
        except Exception:
            text = "[PDF-Kontext konnte nicht automatisch ausgelesen werden.]"
    else:
        text = raw.decode("utf-8", errors="ignore")

    text = text.strip()
    if not text:
        return ""
    return f"--- {label}: {uploaded_file.name} ---\n{text}\n"


def readiness_items() -> list[tuple[str, bool, str]]:
    return [
        ("HuggingFace Token", bool(os.getenv("HF_TOKEN")), "für WhisperX Diarization"),
        ("Codex Modell", bool(os.getenv("CODEX_MODEL", "gpt-5.5")), os.getenv("CODEX_MODEL", "gpt-5.5")),
        (
            "AutoProtocol STT",
            True,
            f"{os.getenv('AUTOPROTOCOL_WHISPER_MODEL', 'large-v3')} / "
            f"{os.getenv('AUTOPROTOCOL_WHISPER_DEVICE', 'cuda')} / "
            f"{os.getenv('AUTOPROTOCOL_WHISPER_COMPUTE_TYPE', 'float16')}",
        ),
        ("Output Ordner", OUTPUT_DIR.exists() or True, str(OUTPUT_DIR)),
    ]


def inject_mobile_css() -> None:
    st.markdown(
        """
        <style>
          :root { color-scheme: light dark; }
          .block-container {
            max-width: 1180px;
            padding-top: 1.25rem;
            padding-bottom: 5rem;
          }
          div[data-testid="stFileUploader"] section {
            min-height: 7rem;
            border-radius: 1.25rem;
            border-style: dashed;
          }
          div[data-testid="stFileUploader"] button,
          .stButton > button,
          .stDownloadButton > button {
            min-height: 3.25rem;
            border-radius: 999px;
            font-weight: 700;
          }
          textarea, input, select {
            font-size: 16px !important; /* verhindert iOS Auto-Zoom */
          }
          .mobile-card {
            border: 1px solid rgba(128,128,128,.25);
            border-radius: 1.25rem;
            padding: 1rem;
            background: rgba(128,128,128,.06);
          }
          @media (max-width: 768px) {
            .block-container { padding-left: 0.85rem; padding-right: 0.85rem; }
            h1 { font-size: 1.7rem !important; line-height: 1.15; }
            h2, h3 { font-size: 1.15rem !important; }
            div[data-testid="column"] { width: 100% !important; flex: 1 1 100% !important; }
          }
        </style>
        """,
        unsafe_allow_html=True,
    )


st.set_page_config(
    page_title="Autoprotocol Web-App",
    page_icon="🎙️",
    layout="wide",
    initial_sidebar_state="expanded",
)
inject_mobile_css()

st.title("🎙️ Autoprotocol Web-App")
st.caption("iPhone/iPad-tauglicher Upload für Voice-Notizen, Audio und Videos — Protokollgenerierung via ChatGPT 5.5/CodexCLI.")

with st.sidebar:
    st.header("Status")
    for name, ok, detail in readiness_items():
        st.write(("✅" if ok else "⚠️") + f" **{name}**")
        st.caption(detail)
    st.divider()
    st.markdown(
        """
        **Mobile Hinweise**
        - iPhone Voice Memos: teilen → In Dateien sichern → hier hochladen.
        - Videos aus Fotos/Dateien werden als `.mov`/`.mp4` akzeptiert.
        - Große Videos bitte im WLAN/Tailnet laden; die Verarbeitung kann dauern.
        """
    )

INPUT_DIR.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

left, right = st.columns([1.25, 1], gap="large")

with left:
    st.subheader("1. Aufnahme hochladen")
    st.markdown('<div class="mobile-card">', unsafe_allow_html=True)
    media_file = st.file_uploader(
        "Audio/Video auswählen oder vom iPhone/iPad aus Dateien/Fotos hochladen",
        type=MEDIA_TYPES,
        accept_multiple_files=False,
        help="Unterstützt u. a. iPhone Voice Memos (.m4a), Videos (.mov/.mp4), WAV/MP3 und WebM.",
    )
    if media_file:
        size_mb = len(media_file.getbuffer()) / 1024 / 1024
        st.success(f"Bereit: {media_file.name} ({size_mb:.1f} MB)")
    st.markdown('</div>', unsafe_allow_html=True)

    st.subheader("2. Sitzungs-Details")
    session_types = load_session_types()
    selected_type = st.selectbox(
        "Art der Sitzung",
        options=list(session_types.keys()),
        format_func=lambda x: session_types[x],
    )
    teilnehmer = st.text_area(
        "Teilnehmer & Funktionen / Namens-Mapping",
        placeholder="Beispiel: SPEAKER_00 = Hans Müller, Präsident\nSPEAKER_01 = Anna Keller, Projektleitung",
        height=130,
    )

with right:
    st.subheader("3. Kontext optional")
    teams_transcript = st.file_uploader(
        "Teams-/Zoom-Transkript oder Notizen",
        type=["vtt", "docx", "txt", "md"],
        help="Hilft beim Namen-Mapping und bei bereits vorhandenen Transkripten.",
    )
    agenda = st.file_uploader("Traktandenliste / Agenda", type=CONTEXT_TYPES)
    last_protocol = st.file_uploader("Letztes Protokoll als Referenz", type=CONTEXT_TYPES)

    with st.expander("Datenschutz & Betrieb", expanded=False):
        st.write(
            "Uploads werden lokal auf diesem JARVIS-Host unter `input/` gespeichert; fertige Protokolle unter `output/`. "
            "Keine Rohdaten gehören ins Git. Zugriff von unterwegs erfolgt über dein Tailnet."
        )

st.divider()

if st.button("🚀 Protokoll generieren", type="primary", use_container_width=True):
    if not media_file:
        st.error("Bitte zuerst eine Audio- oder Videodatei hochladen.")
        st.stop()
    if not os.getenv("HF_TOKEN"):
        st.error("HF_TOKEN fehlt in `.env`. Ohne Token kann WhisperX keine Sprecher-Diarisierung starten.")
        st.stop()

    saved_media_path = save_upload(media_file, INPUT_DIR)
    context_text = "\n".join(
        part
        for part in [
            read_text_upload(teams_transcript, "Teams-/Zoom-Transkript"),
            read_text_upload(agenda, "Traktanden"),
            read_text_upload(last_protocol, "Letztes Protokoll"),
        ]
        if part
    )

    st.info(f"Starte Verarbeitung: {media_file.name} als {session_types[selected_type]}.")

    with st.status("Verarbeitung läuft…", expanded=True) as status:
        st.write("1/3 Datei gespeichert und vorbereitet.")
        try:
            from autoprotocol import process_direct

            st.write("2/3 WhisperX transkribiert Audio/Video. Das kann bei langen Aufnahmen dauern.")
            transcript_data = process_direct(str(saved_media_path))
            from autoprotocol import export_to_word

            transcript_path = export_to_word(transcript_data, media_file.name)
            st.write(f"2/3 Roh-Transkript gesichert: {Path(transcript_path).name}")

            st.write("3/3 ChatGPT 5.5 via CodexCLI erstellt das Protokoll.")
            from llm_processor import generate_protocol_with_llm, save_llm_to_word

            llm_output = generate_protocol_with_llm(
                transcription_data=transcript_data,
                session_type=selected_type,
                participants_text=teilnehmer,
                context_text=context_text,
            )
            word_path = save_llm_to_word(llm_output, media_file.name)
            status.update(label="Protokoll erfolgreich generiert", state="complete")
        except Exception as exc:
            status.update(label="Verarbeitung fehlgeschlagen", state="error")
            st.exception(exc)
            st.stop()

    st.success("🎉 Protokoll erfolgreich generiert!")
    with open(word_path, "rb") as file:
        st.download_button(
            label="📄 Fertiges Protokoll (.docx) herunterladen",
            data=file,
            file_name=Path(word_path).name,
            mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            use_container_width=True,
        )

with st.expander("Letzte erzeugte Protokolle", expanded=False):
    docs = sorted(OUTPUT_DIR.glob("*.docx"), key=lambda p: p.stat().st_mtime, reverse=True)[:10]
    if not docs:
        st.caption("Noch keine Protokolle vorhanden.")
    for doc_path in docs:
        with doc_path.open("rb") as f:
            st.download_button(
                label=f"⬇️ {doc_path.name}",
                data=f,
                file_name=doc_path.name,
                mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                key=f"download-{doc_path.name}",
                use_container_width=True,
            )
