import os
import logging
from typing import List, Optional

# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("response_iq_api")

from fastapi import FastAPI, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field

# Import analysis logic + language registry
try:
    from response_iq_analysis import (
        analyze_response,
        LANGUAGE_CONFIG,
        DEFAULT_LANGUAGE,
        SUPPORTED_LANGUAGES,
        OLLAMA_MODEL,
    )
except ImportError:
    import sys
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    from response_iq_analysis import (
        analyze_response,
        LANGUAGE_CONFIG,
        DEFAULT_LANGUAGE,
        SUPPORTED_LANGUAGES,
        OLLAMA_MODEL,
    )

# Import TTS logic + language registry
try:
    from response_iq_tts import (
        generate_tts,
        TTS_LANGUAGE_CONFIG,
        TTS_SUPPORTED_LANGUAGES,
        TTS_DEFAULT_LANGUAGE,
        TTS_MODEL_ID,
    )
except ImportError:
    import sys
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    from response_iq_tts import (
        generate_tts,
        TTS_LANGUAGE_CONFIG,
        TTS_SUPPORTED_LANGUAGES,
        TTS_DEFAULT_LANGUAGE,
        TTS_MODEL_ID,
    )


# ---------------------------------------------------------------------------
# Pydantic models — Analysis
# ---------------------------------------------------------------------------

class ResponseIQRequest(BaseModel):
    """
    Request payload for the /response_iq_data endpoint.

    Fields
    ------
    question          : The interview / assessment question.
    predefinedAnswer  : The ideal / expected answer.
    userAnswer        : The candidate's actual text answer.
    userVoice         : Optional — URL or Base64-encoded audio of the candidate's voice.
    language          : Language key controlling the script and output language.
                        Supported values: english, hindi, marathi, tamil,
                        telugu, kannada, punjabi.
                        Defaults to 'english' when omitted.
    """

    question:         str           = Field(...,  description="The interview / assessment question.")
    predefinedAnswer: str           = Field(...,  description="The ideal / expected answer.")
    userAnswer:       str           = Field(...,  description="The candidate's actual answer.")
    userVoice:        Optional[str] = Field(None, description="URL or Base64 audio of the candidate's voice.")
    language:         Optional[str] = Field(
        default=DEFAULT_LANGUAGE,
        description=(
            f"Language of the content. "
            f"Supported: {SUPPORTED_LANGUAGES}. "
            f"Defaults to '{DEFAULT_LANGUAGE}'."
        ),
    )

    class Config:
        extra = "ignore"   # Silently ignore any unexpected keys


class ResponseIQResponse(BaseModel):
    """
    Structured analysis result returned by /response_iq_data.

    Fields
    ------
    matchScore        : Semantic similarity score 0–100.
    comparison        : Rationale for the score (in requested language).
    behavioralAnalysis: Communication style insights (in requested language).
    tonalAnalysis     : Voice delivery or text-tone analysis (in requested language).
    visuals           : Exactly 3 vibe keywords (in requested language).
    """

    matchScore:         int           = Field(...,  description="Semantic match score (0–100).")
    comparison:         str           = Field(...,  description="Score rationale in the requested language.")
    behavioralAnalysis: str           = Field(...,  description="Communication style insights.")
    tonalAnalysis:      Optional[str] = Field(None, description="Voice delivery or text-tone analysis.")
    visuals:            List[str]     = Field(...,  description="3 vibe keywords in the requested language.")
    voiceTranscription: Optional[str] = Field(None, description="Whisper transcription of the user's voice (when userVoice is provided).")


class SupportedLanguageItem(BaseModel):
    key:          str = Field(..., description="Language key to pass in the 'language' field.")
    display_name: str = Field(..., description="Human-readable language name.")
    script_name:  str = Field(..., description="Language and script description.")


class SupportedLanguagesResponse(BaseModel):
    supported_languages: List[SupportedLanguageItem]
    default_language:    str


# ---------------------------------------------------------------------------
# Pydantic models — TTS
# ---------------------------------------------------------------------------

class ResponseIQTTSRequest(BaseModel):
    """
    Request payload for the /response_iq_tts endpoint.

    Fields
    ──────
    predefinedAnswer : The text to be converted to speech.
                       Typically the ideal/model answer for a question.

    language         : Language key that controls the voice, accent, and
                       script used during synthesis.
                       Supported values: english, hinglish, hindi, marathi,
                       punjabi, tamil, telugu, kannada, gujarati, bengali,
                       odia, assamese, maithili, urdu.
                       Defaults to 'english' when omitted.
    """

    predefinedAnswer: str           = Field(
        ...,
        description="The text to synthesise into speech.",
        min_length=1,
    )
    language:         Optional[str] = Field(
        default=TTS_DEFAULT_LANGUAGE,
        description=(
            f"Target language for TTS. "
            f"Supported: {TTS_SUPPORTED_LANGUAGES}. "
            f"Defaults to '{TTS_DEFAULT_LANGUAGE}'."
        ),
    )

    class Config:
        extra = "ignore"


class ResponseIQTTSResponse(BaseModel):
    """
    TTS result returned by /response_iq_tts.

    Fields
    ──────
    audio_tts    : Base64-encoded WAV audio string. Decode and play directly.
    language     : Normalised language key used for synthesis.
    sample_rate  : Sample rate of the returned audio (Hz).
    duration_sec : Duration of the synthesised audio in seconds.
    char_count   : Number of characters that were synthesised.
    """

    audio_tts:    str   = Field(...,  description="Base64-encoded WAV audio of the synthesised speech.")
    language:     str   = Field(...,  description="Language key used for synthesis.")
    sample_rate:  int   = Field(...,  description="Audio sample rate in Hz.")
    duration_sec: float = Field(...,  description="Duration of synthesised audio in seconds.")
    char_count:   int   = Field(...,  description="Number of characters synthesised.")


class TTSSupportedLanguageItem(BaseModel):
    key:          str = Field(..., description="Language key to pass in the 'language' field.")
    display_name: str = Field(..., description="Human-readable language name.")
    bcp47_tag:    str = Field(..., description="BCP-47 language tag used for synthesis.")


class TTSSupportedLanguagesResponse(BaseModel):
    supported_languages: List[TTSSupportedLanguageItem]
    default_language:    str
    model_id:            str


# ---------------------------------------------------------------------------
# FastAPI application
# ---------------------------------------------------------------------------

app = FastAPI(
    title="Response IQ API",
    description=(
        "## Response IQ — Intelligent Answer Analysis & Text-to-Speech\n\n"
        "### Analysis (`/response_iq_data`)\n"
        f"Analyses a candidate's interview answer against an ideal answer using the "
        f"**{OLLAMA_MODEL}** model via Ollama. "
        "Supports multi-language content and optional voice tonal analysis.\n\n"
        f"**Supported analysis languages:** {', '.join(SUPPORTED_LANGUAGES)}\n\n"
        "---\n\n"
        "### Text-to-Speech (`/response_iq_tts`)\n"
        f"Converts text to natural human-like speech using **Indic Parler-TTS** "
        f"(`{TTS_MODEL_ID}`) running on the local GPU server. "
        "Returns Base64-encoded WAV audio.\n\n"
        f"**Supported TTS languages:** {', '.join(TTS_SUPPORTED_LANGUAGES)}"
    ),
    version="3.0.0",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# ---------------------------------------------------------------------------
# Endpoints — Analysis
# ---------------------------------------------------------------------------

@app.post(
    "/response_iq_data",
    response_model=ResponseIQResponse,
    summary="Analyse a candidate's response",
    description=(
        "Accepts a question, the ideal answer, and the candidate's answer. "
        "Optionally accepts a voice recording (URL or Base64). "
        "The **`language`** field controls which language the analysis is written in. "
        f"Supported values: `{SUPPORTED_LANGUAGES}`. "
        "Returns a structured evaluation with match score, behavioural analysis, "
        "tonal analysis, and 3 vibe keywords — all in the requested language."
    ),
    responses={
        200: {"description": "Successful analysis result."},
        400: {"description": "Unsupported language key supplied."},
        500: {"description": "LLM or audio-processing error."},
    },
    tags=["Analysis"],
)
async def response_iq_data_endpoint(payload: ResponseIQRequest):
    """
    1. Validates the language key.
    2. Optionally extracts acoustic features from the voice recording.
    3. Calls the LLM to produce a language-aware analysis.
    4. Returns the structured result.
    """

    # ── Normalise and validate language ──────────────────────────────────
    requested_language = (payload.language or DEFAULT_LANGUAGE).strip().lower()

    if requested_language not in LANGUAGE_CONFIG:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=(
                f"Unsupported language '{requested_language}'. "
                f"Valid values are: {SUPPORTED_LANGUAGES}."
            ),
        )

    logger.info(
        f"POST /response_iq_data | language={requested_language} | "
        f"has_voice={'yes' if payload.userVoice else 'no'} | "
        f"question='{payload.question[:50]}...'"
    )

    # ── Run analysis ──────────────────────────────────────────────────────
    analysis_result = analyze_response(
        question=payload.question,
        predefined_answer=payload.predefinedAnswer,
        user_answer=payload.userAnswer,
        user_voice_url=payload.userVoice,
        language=requested_language,
    )

    # ── Surface LLM / processing errors as HTTP 500 ───────────────────────
    if "error" in analysis_result:
        logger.error(f"Analysis failed: {analysis_result['error']}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=analysis_result["error"],
        )

    return analysis_result


# ---------------------------------------------------------------------------
# Endpoints — Text-to-Speech
# ---------------------------------------------------------------------------

@app.post(
    "/response_iq_tts",
    response_model=ResponseIQTTSResponse,
    summary="Convert predefined answer text to speech",
    description=(
        "Synthesises the `predefinedAnswer` text into natural, human-like speech "
        "using **Indic Parler-TTS** (`ai4bharat/indic-parler-tts`) running on the "
        "local GPU server.\n\n"
        "The `language` field selects the speaker's accent, voice profile, and "
        "script-aware phoneme rendering. "
        f"Supported values: `{TTS_SUPPORTED_LANGUAGES}`.\n\n"
        "The response contains a **Base64-encoded WAV** string in `audio_tts`. "
        "Decode it on the client and play directly, or save as a `.wav` file.\n\n"
        "**Example client usage (PHP):**\n"
        "```php\n"
        "$audioData = base64_decode($response['audio_tts']);\n"
        "file_put_contents('answer.wav', $audioData);\n"
        "```\n\n"
        "**Example client usage (JavaScript):**\n"
        "```js\n"
        "const blob = new Blob(\n"
        "  [Uint8Array.from(atob(response.audio_tts), c => c.charCodeAt(0))],\n"
        "  { type: 'audio/wav' }\n"
        ");\n"
        "const url = URL.createObjectURL(blob);\n"
        "new Audio(url).play();\n"
        "```"
    ),
    responses={
        200: {"description": "Base64 WAV audio returned successfully."},
        400: {"description": "Unsupported language key or empty text supplied."},
        500: {"description": "TTS model error or GPU processing failure."},
    },
    tags=["Text-to-Speech"],
)
async def response_iq_tts_endpoint(payload: ResponseIQTTSRequest):
    """
    1. Validates the language key against TTS_LANGUAGE_CONFIG.
    2. Normalises and chunks the predefinedAnswer text.
    3. Synthesises each chunk via Indic Parler-TTS (GPU inference).
    4. Concatenates chunks, resamples, encodes as Base64 WAV.
    5. Returns the structured TTS result.
    """

    # ── Normalise and validate language ──────────────────────────────────
    requested_language = (payload.language or TTS_DEFAULT_LANGUAGE).strip().lower()

    if requested_language not in TTS_LANGUAGE_CONFIG:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=(
                f"Unsupported language '{requested_language}' for TTS. "
                f"Valid values are: {TTS_SUPPORTED_LANGUAGES}."
            ),
        )

    # ── Validate text payload ─────────────────────────────────────────────
    text = (payload.predefinedAnswer or "").strip()
    if not text:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="'predefinedAnswer' must not be empty.",
        )

    logger.info(
        f"POST /response_iq_tts | language={requested_language} | "
        f"chars={len(text)} | preview='{text[:60]}{'...' if len(text) > 60 else ''}'"
    )

    # ── Run TTS synthesis ─────────────────────────────────────────────────
    tts_result = generate_tts(
        text=text,
        language=requested_language,
    )

    # ── Surface TTS errors as HTTP 500 ───────────────────────────────────
    if "error" in tts_result:
        logger.error(f"TTS failed: {tts_result['error']}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=tts_result["error"],
        )

    logger.info(
        f"TTS response ready | language={requested_language} | "
        f"duration={tts_result.get('duration_sec')}s | "
        f"base64_len={len(tts_result.get('audio_tts', ''))}"
    )

    return tts_result


# ---------------------------------------------------------------------------
# Endpoints — Metadata / Discovery
# ---------------------------------------------------------------------------

@app.get(
    "/supported_languages",
    response_model=SupportedLanguagesResponse,
    summary="List supported analysis languages",
    description=(
        "Returns all language keys accepted by the `language` field of `/response_iq_data`, "
        "along with their display names and script descriptions. "
        "Use this to populate language selector dropdowns in your frontend."
    ),
    tags=["Metadata"],
)
async def supported_languages_endpoint():
    """Returns every supported analysis language key along with human-readable metadata."""
    return {
        "supported_languages": [
            {
                "key":          key,
                "display_name": cfg["display_name"],
                "script_name":  cfg["script_name"],
            }
            for key, cfg in LANGUAGE_CONFIG.items()
        ],
        "default_language": DEFAULT_LANGUAGE,
    }


@app.get(
    "/tts_supported_languages",
    response_model=TTSSupportedLanguagesResponse,
    summary="List supported TTS languages",
    description=(
        "Returns all language keys accepted by the `language` field of `/response_iq_tts`, "
        "along with display names and BCP-47 tags. "
        "Use this to populate language selector dropdowns in your frontend."
    ),
    tags=["Metadata"],
)
async def tts_supported_languages_endpoint():
    """Returns every supported TTS language key along with human-readable metadata."""
    return {
        "supported_languages": [
            {
                "key":          key,
                "display_name": cfg["display_name"],
                "bcp47_tag":    cfg["bcp47_lang_tag"],
            }
            for key, cfg in TTS_LANGUAGE_CONFIG.items()
        ],
        "default_language": TTS_DEFAULT_LANGUAGE,
        "model_id":         TTS_MODEL_ID,
    }


@app.get(
    "/health",
    summary="Health check",
    description="Returns API status, active LLM model name, and TTS model name.",
    tags=["Metadata"],
)
async def health_check():
    """Simple liveness probe."""
    return {
        "status":                    "ok",
        "llm_model":                 OLLAMA_MODEL,
        "tts_model":                 TTS_MODEL_ID,
        "supported_languages":       SUPPORTED_LANGUAGES,
        "tts_supported_languages":   TTS_SUPPORTED_LANGUAGES,
    }