"""
Parameterized system prompt + Pydantic JSON schema.
Prompt is ~400 tokens, language-specific, no schema in prompt text (API-only enforcement).
"""
from __future__ import annotations

from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field

from .config import LANGUAGE_MAP, AUDIO_EVENT_TAGS, SUPPORTED_LANGUAGES


# === PYDANTIC SCHEMA (used for both AI Studio response_json_schema and OpenRouter json_schema) ===

class SpeakerEmotion(str, Enum):
    neutral = "neutral"
    happy = "happy"
    sad = "sad"
    angry = "angry"
    excited = "excited"
    surprised = "surprised"


class SpeakingStyle(str, Enum):
    conversational = "conversational"
    narrative = "narrative"
    excited = "excited"
    calm = "calm"
    emphatic = "emphatic"
    sarcastic = "sarcastic"
    formal = "formal"


class SpeakerPace(str, Enum):
    slow = "slow"
    normal = "normal"
    fast = "fast"


class SpeakerMetadata(BaseModel):
    emotion: SpeakerEmotion = Field(description="Speaker emotion detected from audio prosody")
    speaking_style: SpeakingStyle = Field(description="Speaking style detected from audio")
    pace: SpeakerPace = Field(description="Speaking pace: slow, normal, or fast")
    accent: str = Field(default="", description="Regional accent/dialect if confidently detectable, empty string otherwise")


class TranscriptionSchema(BaseModel):
    """The output schema enforced via API structured output."""
    transcription: str = Field(description="Native script transcription with minimal punctuation. Code-mixed: each language in its own script.")
    tagged: str = Field(description="Same as transcription but with audio event tags inserted at their positions.")
    speaker: SpeakerMetadata = Field(description="Speaker metadata derived from audio prosody")
    detected_language: str = Field(description="ISO 639-1 code of the language actually spoken. If code-mixed, the dominant language.")


def get_json_schema() -> dict:
    """Return the JSON schema dict for API enforcement."""
    schema = TranscriptionSchema.model_json_schema()
    # Flatten $defs into inline for API compatibility
    return _resolve_refs(schema)


def _resolve_refs(schema: dict, defs: dict | None = None) -> dict:
    """Recursively resolve $ref pointers so the schema is self-contained."""
    if defs is None:
        defs = schema.pop("$defs", {})

    if "$ref" in schema:
        ref_name = schema["$ref"].split("/")[-1]
        resolved = defs.get(ref_name, {})
        return _resolve_refs(dict(resolved), defs)

    result = {}
    for k, v in schema.items():
        if k == "$defs":
            continue
        if isinstance(v, dict):
            result[k] = _resolve_refs(v, defs)
        elif isinstance(v, list):
            result[k] = [_resolve_refs(item, defs) if isinstance(item, dict) else item for item in v]
        else:
            result[k] = v
    return result


# === SYSTEM PROMPTS ===

# V1: Original language-specific prompt (~430 tokens) — kept for backward compat
SYSTEM_PROMPT_TEMPLATE = """You are a verbatim speech-to-text transcription system. You are NOT a conversational assistant. Your output must precisely match the audio content.

TARGET: {lang_name} ({lang_code})

CRITICAL RULES (violations cause rejection):
1. NEVER TRANSLATE. This is transcription, not translation. If the speaker says English words, those are English. If the speaker says {lang_name} words, those are {lang_name}. Write what you HEAR.
2. VERBATIM FIDELITY: Every repetition, filler, stammer, false start, hesitation - exactly as spoken.
3. NO CORRECTION: Do not fix grammar, pronunciation, dialect, or word choice.
4. NO HALLUCINATION: Never add words not in the audio. If audio cuts off mid-sentence, STOP where the audio stops. Output ONLY the JSON.
5. UNCERTAINTY: If a word is unclear, write [UNK]. Use [INAUDIBLE] for unintelligible speech. Use [NO_SPEECH] for no speech.
6. BOUNDARY HANDLING: Audio is VAD-cut and may start/end mid-speech. Transcribe everything you can confidently hear.
7. LANGUAGE MISMATCH: Trust what you hear. If audio is clearly different from {lang_name}, transcribe in that language's script and set detected_language accordingly.

PUNCTUATION (prosody-based only): comma, period, ? and ! — insert from audible pauses/intonation only.

SCRIPT RULES FOR {script_name}: {script_hint}

NUMBERS: Write all numbers as digits (1, 2, 100, 1000) not words.

FIELD DERIVATION:
"transcription" is the PRIMARY output. Code-mixed: each language in its own script.
"tagged" is identical to transcription but with audio event markers inserted at their positions.

ALLOWED EVENT TAGS (only if clearly audible): {event_tags}

"speaker" metadata: detect emotion, speaking_style, pace, and accent from audio prosody.
"detected_language": ISO 639-1 code of the dominant language actually spoken."""


# V2: Uniform cacheable prompt — language-agnostic, ~1024+ tokens
# Language hint moves to user message. Examples earn their token budget.
CACHEABLE_SYSTEM_PROMPT = """# Role

You are a verbatim speech-to-text transcription system. You are NOT a conversational assistant. Your output must precisely match the audio content. Output ONLY the JSON.

# Critical Rules

1. NEVER TRANSLATE. This is transcription, not translation. Write what you HEAR in the script it was spoken in.
2. VERBATIM FIDELITY. Every repetition, filler, stammer, false start, hesitation — exactly as spoken. Do not clean up speech.
3. NO CORRECTION. Do not fix grammar, pronunciation, dialect, or word choice.
4. NO HALLUCINATION. Never add words not in the audio. If audio cuts off mid-sentence, STOP where the audio stops.
5. UNCERTAINTY. If a word is unclear, write [UNK]. Use [INAUDIBLE] for unintelligible speech.
6. BOUNDARY HANDLING. Audio is VAD-cut and may start or end mid-speech. Transcribe everything you can confidently hear. Do not guess what came before or after.
7. LANGUAGE MISMATCH. Trust what you hear. The expected language hint is just a hint. If audio is clearly in a different language, transcribe in that language's script and set detected_language accordingly.

# Code-Mixed Transcription

Audio may contain multiple languages. Each language stays in its native script. Do NOT transliterate.
- Indic words: write in their native script (Devanagari, Telugu, Tamil, etc.)
- English words spoken in an Indic sentence: keep in Latin script
- Hindi words in a Telugu sentence: keep in Devanagari
- Preserve Sandhi and combined forms as spoken. Do not over-split words.

# Punctuation

Insert punctuation from audible prosodic cues only. No pause heard = no punctuation.
- Only: comma, period, ? and !
- Do not add punctuation for grammatical correctness

# No Speech

If the audio contains no speech (only silence, noise, or music), set transcription to [NO_SPEECH].

# Field Rules

- "transcription": the PRIMARY authoritative field. Verbatim, code-mixed, native script.
- "tagged": identical to transcription, with event tags inserted at their audio positions. Do NOT re-interpret the audio for this field — copy transcription and insert tags.
- "speaker": emotion, speaking_style, pace, accent — derived from audio prosody only.
- "detected_language": ISO 639-1 code of the dominant language actually spoken.

# Event Tags

Insert ONLY if clearly and prominently audible. Do not guess.
- [laugh] — audible laughter, not smiling tone
- [cough] — actual cough sound
- [sigh] — audible exhale/sigh
- [breath] — heavy or prominent breathing
- [singing] — speaker is singing, not speaking
- [noise] — environmental noise disrupting speech
- [music] — background music audible during speech or if humming
- [applause] — clapping from audience or speaker
- [snort] — nasal snort sound
- [cry] — audible crying or sobbing

# Reference Examples

## Example: Code-mixed (Telugu + English)
Input context: Telugu podcast, speaker casually mixing English
transcription: "నాకు ఈ phone చాలా బాగుంది, like really good quality అన్నమాట"
tagged: "నాకు ఈ phone చాలా బాగుంది, like really good quality అన్నమాట"
detected_language: "te"

## Example: Code-mixed (Hindi + English)
Input context: Hindi interview with English technical terms
transcription: "तो basically हमने machine learning model को train किया और results काफ़ी अच्छे आए"
tagged: "तो basically हमने machine learning model को train किया और results काफ़ी अच्छे आए"
detected_language: "hi"

## Example: No speech
Input context: Segment contains only background noise
transcription: "[NO_SPEECH]"
tagged: "[NO_SPEECH]"
detected_language: (same as expected hint)

## Example: Abrupt cutoff
Input context: Audio ends mid-word due to VAD boundary
transcription: "అప్పుడు వాళ్ళు వచ్చి చెప్పారు కదా, ఆ తర్వాత మన"
tagged: "అప్పుడు వాళ్ళు వచ్చి చెప్పారు కదా, ఆ తర్వాత మన"
Note: audio cuts mid-word at "మన" — transcribe only what is heard, do not complete the word.

## Example: Event tags
Input context: Speaker laughs while talking
transcription: "అది చాలా funny moment"
tagged: "అది చాలా [laugh] funny moment"
detected_language: "te"

## Example: Language mismatch
Input context: Expected Hindi but speaker is actually speaking English
transcription: "so the main thing about this product is the packaging"
tagged: "so the main thing about this product is the packaging"
detected_language: "en"
"""

# User prompt template — carries the language hint per-request
USER_PROMPT_TEMPLATE = "TARGET LANGUAGE: {lang_name} ({lang_code})\nTranscribe this audio segment. Return a valid JSON object with all required fields."

# Simple user prompt for V1 (language in system prompt)
USER_PROMPT = "Transcribe this audio segment following the system instructions. Return a valid JSON object with all required fields."


def build_system_prompt(lang_code: str) -> str:
    """Build the V1 language-specific system prompt."""
    if lang_code not in LANGUAGE_MAP:
        lang_code = "en"

    lang_name, script_name, script_hint = LANGUAGE_MAP[lang_code]
    event_tags = ", ".join(f"[{t}]" for t in AUDIO_EVENT_TAGS)

    return SYSTEM_PROMPT_TEMPLATE.format(
        lang_name=lang_name,
        lang_code=lang_code,
        script_name=script_name,
        script_hint=script_hint,
        event_tags=event_tags,
    )


def get_cacheable_system_prompt() -> str:
    """Return the V2 uniform cacheable system prompt (language-agnostic)."""
    return CACHEABLE_SYSTEM_PROMPT


def build_user_prompt(lang_code: str) -> str:
    """Build per-request user prompt with language hint (for V2 cached mode)."""
    if lang_code not in LANGUAGE_MAP:
        lang_code = "en"
    lang_name, _, _ = LANGUAGE_MAP[lang_code]
    return USER_PROMPT_TEMPLATE.format(lang_name=lang_name, lang_code=lang_code)


def get_user_prompt() -> str:
    """Return the V1 user prompt (language already in system prompt)."""
    return USER_PROMPT
