"""
Service de transcription audio via Gemini 2.5 Flash (google-genai SDK).

Interface identique à whisper_service.py — remplaçable dans le pipeline
en swappant l'import et l'instance sans toucher au reste du code.

Usage :
    from app.services.gemini_service import gemini_service

    with open("audio.ogg", "rb") as f:
        result = await gemini_service.transcribe(f.read(), filename="audio.ogg")

    print(result.transcript)
    print(f"{result.duration_seconds}s")
"""

import logging
import os
import re
import time
from dataclasses import dataclass
from typing import Optional

from google import genai
from google.genai import types

from app.core.config import settings

logger = logging.getLogger(__name__)


# Seuil minimal : si le transcript nettoyé contient moins de 10
# caractères significatifs, on le considère comme vide.
MIN_TRANSCRIPT_LENGTH = 10


def _clean_transcript(text: str) -> str:
    cleaned = re.sub(r'\[.*?\]', '', text)
    cleaned = re.sub(r'\s+', ' ', cleaned).strip()
    if len(cleaned) < MIN_TRANSCRIPT_LENGTH:
        return ""
    return cleaned


BTP_CONTEXT_PROMPT_GEMINI = """ABSOLUTE PRIORITY RULE: if you have ANY doubt about whether \
the audio contains real, clearly intelligible human speech, respond with \
EXACTLY [SILENCE] and nothing else. This includes: total silence, background \
noise, breathing, coughing, faint or ambiguous sounds, a single unclear word, \
or any audio where you are not fully confident about what was said. \
When in doubt, always choose [SILENCE] over producing a transcript. \
Never transcribe something you did not clearly and confidently hear.

You are an audio transcriber for a Moroccan construction worker (BTP sector) \
dictating his professional profile for a CV.

Rules:
- The speaker mixes Moroccan Darija, French, and sometimes Modern Standard \
Arabic in the same sentence (natural code-switching).
- Transcribe Darija phonetically in LATIN script, never in Arabic script.
- If the speaker says words in French, transcribe them in French as spoken.
- If the speaker says words in Modern Standard Arabic, transcribe them in \
Arabic script only for that specific portion.
- Do NOT translate anything. Do NOT add content not present in the audio.
- Do NOT summarize. Transcribe exactly what is said, word for word.
- Numbers must always be written as Latin digits (1, 2, 3...), never spelled \
out in any language.
- Common BTP trades to expect: maçon, coffreur, ferrailleur, électricien, \
plombier, soudeur, peintre, carreleur, chef de chantier, chef d'équipe, \
conducteur de travaux, manœuvre.
- IMPORTANT: the terms in this glossary are recognition aids for words that \
were actually spoken. NEVER produce a glossary term that was not actually \
said in the audio. Reciting, listing, or rephrasing this glossary in the \
transcript is forbidden.
- Common Moroccan training acronyms to expect: OFPPT, CAP, BTS, CFA, ISTA.
- Common Moroccan cities to expect: Casablanca, Rabat, Tanger, Marrakech, \
Agadir, Fès, Mohammedia.
- CONFIDENCE CHECK: a short audio clip (a few seconds) cannot contain a long, \
detailed, multi-sentence professional narrative. If your draft transcript is \
long and detailed but the audio was brief or faint, this is a signal you are \
fabricating — discard it and respond [SILENCE] instead.
- If the audio is unclear or silent at any point, do not invent content — \
transcribe only what is clearly audible.
- If the ENTIRE audio contains no intelligible speech, OR if you are not \
fully confident in what was said, respond with exactly [SILENCE] and \
nothing else. Do NOT attempt to guess or produce plausible content.

Final reminder: any doubt → respond with exactly [SILENCE]. Never fill in \
with invented content, guessed content, or content taken from this prompt's \
glossary.
"""


@dataclass
class TranscriptionResult:
    """Résultat d'une transcription audio."""
    transcript: str
    detected_language: Optional[str]
    duration_seconds: float
    model: str


class GeminiService:
    """
    Wrapper autour de Gemini 2.5 Flash (google-genai SDK).

    Instance unique partagée via la variable `gemini_service` en bas du module.
    Interface identique à WhisperService pour substitution directe dans le pipeline.
    """

    def __init__(self):
        if not settings.GEMINI_API_KEY:
            raise ValueError(
                "GEMINI_API_KEY manquante dans .env. "
                "Récupère-la sur https://aistudio.google.com/apikey"
            )
        self.client = genai.Client(api_key=settings.GEMINI_API_KEY)
        self.model = "gemini-2.5-flash"

    _MIME_TYPES = {
        ".ogg": "audio/ogg",
        ".m4a": "audio/mp4",
        ".mp3": "audio/mpeg",
        ".wav": "audio/wav",
        ".opus": "audio/opus",
        ".webm": "audio/webm",
        ".flac": "audio/flac",
    }

    async def transcribe(
        self,
        audio_bytes: bytes,
        filename: str = "audio.ogg",
        prompt: Optional[str] = BTP_CONTEXT_PROMPT_GEMINI,
    ) -> TranscriptionResult:
        """
        Transcrit un fichier audio en texte via envoi inline (base64).

        Args:
            audio_bytes : Le contenu binaire du fichier audio.
            filename    : Nom du fichier (extension utilisée pour déduire le mime_type).
            prompt      : Prompt système envoyé à Gemini, ou None pour désactiver.

        Returns:
            TranscriptionResult avec transcript, langue détectée (None), durée.

        Raises:
            Exception : si l'appel Gemini échoue.
        """
        start_time = time.time()

        ext = os.path.splitext(filename)[-1].lower()
        mime_type = self._MIME_TYPES.get(ext, "audio/ogg")

        audio_part = types.Part.from_bytes(data=audio_bytes, mime_type=mime_type)

        contents = [audio_part]
        if prompt:
            contents.append(prompt)

        try:
            response = self.client.models.generate_content(
                model=self.model,
                contents=contents,
            )
        except Exception:
            raise

        elapsed = time.time() - start_time

        raw_text = response.text or ""
        logger.info("[DIAG] Gemini RAW (repr): %r", raw_text)

        cleaned = _clean_transcript(raw_text)
        logger.info("[DIAG] Gemini CLEANED (repr): %r | len=%d", cleaned, len(cleaned))

        return TranscriptionResult(
            transcript=cleaned,
            detected_language=None,
            duration_seconds=round(elapsed, 2),
            model=self.model,
        )


# Instance globale unique - importée partout dans l'app
gemini_service = GeminiService()
