"""Persian Tutor — Flask backend.

Endpoints:
  GET  /persian              → chat UI
  POST /api/ask              → {"query": "..."} → {"persian", "translit", "english", "notes", "audio_url"}
  POST /api/breakdown        → word-by-word table for a Persian phrase
  GET  /static/audio/<file>  → cached TTS mp3

Setup:
  1. cp .env.template .env  (fill in keys)
  2. pip install -r requirements.txt
  3. python app.py  (or bash run.sh)
"""
import hashlib
import json
import os
import re
from pathlib import Path

import anthropic
import requests
from flask import Flask, jsonify, render_template, request, send_from_directory

import lessons as lessons_store

# ---- Config ----
ROOT = Path(__file__).parent
AUDIO_DIR = ROOT / "static" / "audio"
AUDIO_DIR.mkdir(parents=True, exist_ok=True)
SYSTEM_PROMPT = (ROOT / "prompts" / "tutor_system.md").read_text()

ANTHROPIC_KEY = os.environ["ANTHROPIC_API_KEY"]
ELEVENLABS_KEY = os.environ["ELEVENLABS_API_KEY"]
ELEVENLABS_VOICE_ID = os.environ.get("ELEVENLABS_VOICE_ID", "")  # set after picking a voice
# eleven_v3 is the only ElevenLabs model that actually supports Persian (fa).
# multilingual_v2 lacks Persian and approximates it with Arabic/Hindi phonemes —
# that's the "sounds Arabic/Indian" problem. Cameron confirmed v3 by ear 2026-06-23.
ELEVENLABS_MODEL = os.environ.get("ELEVENLABS_MODEL", "eleven_v3")

CLAUDE_MODEL = "claude-sonnet-4-6"  # update to latest as needed

client = anthropic.Anthropic(api_key=ANTHROPIC_KEY)
app = Flask(__name__, static_url_path="/static", static_folder="static")
app.config["TEMPLATES_AUTO_RELOAD"] = True  # pick up chat.html edits without a restart (debug is off)


# ---- Helpers ----

def claude_ask(user_query: str, history: list = None) -> str:
    """Send query to Claude tutor with system prompt + optional history."""
    messages = (history or []) + [{"role": "user", "content": user_query}]
    resp = client.messages.create(
        model=CLAUDE_MODEL,
        max_tokens=1024,
        system=SYSTEM_PROMPT,
        messages=messages,
    )
    return resp.content[0].text


def parse_tutor_response(text: str) -> dict:
    """Extract structured fields from tutor's markdown response."""
    out = {"persian": "", "translit": "", "english": "", "notes": ""}
    for key, label in [
        ("persian", "Persian"),
        ("translit", "Transliteration"),
        ("english", "English"),
        ("notes", "Notes"),
    ]:
        m = re.search(rf"\*\*{label}:\*\*\s*(.+?)(?=\n\*\*|\Z)", text, re.DOTALL)
        if m:
            out[key] = m.group(1).strip()
    out["raw"] = text
    return out


def elevenlabs_tts(text: str) -> str:
    """Generate Persian (+ optional bilingual) audio. Returns relative URL to audio file."""
    # Content-hash cache so identical phrases reuse audio
    key = hashlib.sha256(text.encode("utf-8")).hexdigest()[:16]
    out_path = AUDIO_DIR / f"{key}.mp3"
    if out_path.exists():
        return f"/persian/static/audio/{key}.mp3"

    if not ELEVENLABS_VOICE_ID:
        raise RuntimeError("ELEVENLABS_VOICE_ID not set — pick a voice at elevenlabs.io/voice-library and set in .env")

    r = requests.post(
        f"https://api.elevenlabs.io/v1/text-to-speech/{ELEVENLABS_VOICE_ID}",
        headers={
            "xi-api-key": ELEVENLABS_KEY,
            "Content-Type": "application/json",
            "Accept": "audio/mpeg",
        },
        json={
            "text": text,
            "model_id": ELEVENLABS_MODEL,
            "voice_settings": {"stability": 0.5, "similarity_boost": 0.75},
        },
        timeout=60,
    )
    r.raise_for_status()
    out_path.write_bytes(r.content)
    return f"/persian/static/audio/{key}.mp3"


# ---- Routes ----

@app.route("/persian")
@app.route("/persian/")
def chat_ui():
    return render_template("chat.html")


@app.post("/persian/api/ask")
def api_ask():
    data = request.get_json()
    query = data.get("query", "").strip()
    history = data.get("history", [])
    if not query:
        return jsonify({"error": "empty query"}), 400

    raw = claude_ask(query, history)
    parsed = parse_tutor_response(raw)

    # Generate audio that includes both Persian + the English framing
    # (ElevenLabs multilingual handles code-switching with one voice)
    audio_text = parsed["persian"]
    if parsed["translit"]:
        # Optionally include English framing for bilingual playback;
        # comment out next line if you want Persian-only audio
        audio_text = f"In Persian, you say: {parsed['persian']}"
    try:
        parsed["audio_url"] = elevenlabs_tts(audio_text) if parsed["persian"] else None
    except Exception as e:
        parsed["audio_url"] = None
        parsed["audio_error"] = str(e)

    return jsonify(parsed)


@app.post("/persian/api/tts")
def api_tts():
    """Synthesize (and cache) audio for an arbitrary phrase. Used by the lessons
    UI to play individual phrases on demand."""
    data = request.get_json()
    text = data.get("text", "").strip()
    if not text:
        return jsonify({"error": "empty text"}), 400
    try:
        return jsonify({"audio_url": elevenlabs_tts(text)})
    except Exception as e:
        return jsonify({"error": str(e)}), 502


@app.route("/persian/lessons")
@app.route("/persian/lessons/")
def lessons_ui():
    return render_template("lessons.html")


@app.get("/persian/api/lessons")
def api_lessons():
    return jsonify(lessons_store.load_index())


@app.get("/persian/api/lessons/<lesson_id>")
def api_lesson(lesson_id):
    lesson = lessons_store.load_lesson(lesson_id)
    if lesson is None:
        return jsonify({"error": "unknown lesson"}), 404
    return jsonify(lesson)


@app.post("/persian/api/breakdown")
def api_breakdown():
    """Word-by-word breakdown for a Persian phrase."""
    data = request.get_json()
    phrase = data.get("phrase", "").strip()
    if not phrase:
        return jsonify({"error": "empty phrase"}), 400

    raw = claude_ask(f"Break down this Persian phrase word-by-word in a markdown table: {phrase}")
    return jsonify({"raw": raw})


@app.get("/persian/health")
@app.get("/health")
def health():
    return {"ok": True, "voice_set": bool(ELEVENLABS_VOICE_ID)}


# Serve all static under the /persian prefix too (so it works behind reverse proxy)
@app.get("/persian/static/<path:filename>")
def persian_static(filename):
    from flask import send_from_directory
    return send_from_directory(ROOT / "static", filename)


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001, debug=False)
