"""
app.py - minimal web wrapper around the TTS/STT test scripts, so they can
be tested from a browser instead of the command line. Built specifically
for cPanel's "Setup Python App" feature (uses Passenger — see
passenger_wsgi.py alongside this file, and README-CPANEL.md for the
exact setup steps).

Two pages:
  /          - type Arabic text, hear it spoken (TTS test)
  /transcribe - upload an audio file, see it transcribed (STT test)

Deliberately kept to one file, no database, no session state — this is
a throwaway test tool, not something meant to become permanent.
"""
import os
import time
import wave
import numpy as np
from flask import Flask, request, send_file, render_template_string

app = Flask(__name__)

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
TTS_MODEL_DIR = os.path.join(BASE_DIR, "vits-piper-ar_JO-kareem-medium")
STT_MODEL_DIR = os.path.join(BASE_DIR, "sherpa-onnx-whisper-small")
STT_MODEL_SIZE = "small"
OUTPUT_DIR = os.path.join(BASE_DIR, "generated")
os.makedirs(OUTPUT_DIR, exist_ok=True)

# Models are loaded once at startup, not per-request — loading an ONNX
# model from disk on every request would make each test painfully slow.
_tts = None
_stt = None


def get_tts():
    global _tts
    if _tts is None:
        import sherpa_onnx
        _tts = sherpa_onnx.OfflineTts(
            sherpa_onnx.OfflineTtsConfig(
                model=sherpa_onnx.OfflineTtsModelConfig(
                    vits=sherpa_onnx.OfflineTtsVitsModelConfig(
                        model=os.path.join(TTS_MODEL_DIR, "ar_JO-kareem-medium.onnx"),
                        lexicon="",
                        tokens=os.path.join(TTS_MODEL_DIR, "tokens.txt"),
                        data_dir=os.path.join(TTS_MODEL_DIR, "espeak-ng-data"),
                    ),
                    num_threads=2,
                ),
            )
        )
    return _tts


def get_stt():
    global _stt
    if _stt is None:
        import sherpa_onnx
        _stt = sherpa_onnx.OfflineRecognizer.from_whisper(
            encoder=os.path.join(STT_MODEL_DIR, f"{STT_MODEL_SIZE}-encoder.int8.onnx"),
            decoder=os.path.join(STT_MODEL_DIR, f"{STT_MODEL_SIZE}-decoder.int8.onnx"),
            tokens=os.path.join(STT_MODEL_DIR, f"{STT_MODEL_SIZE}-tokens.txt"),
            language="ar",
            task="transcribe",
            num_threads=2,
        )
    return _stt


TTS_PAGE = """
<!DOCTYPE html>
<html dir="rtl" lang="ar">
<head><meta charset="UTF-8"><title>Voice Test — Text to Speech</title>
<style>body{font-family:sans-serif;max-width:600px;margin:40px auto;padding:0 20px;}
textarea{width:100%;height:80px;font-size:16px;}button{padding:10px 20px;font-size:16px;margin-top:10px;}
.result{margin-top:20px;padding:15px;background:#f0f0f0;border-radius:8px;}
.timing{color:#666;font-size:13px;}</style>
</head>
<body>
<h1>Voice Test — نص إلى صوت (TTS)</h1>
<p><a href="/transcribe">Switch to Speech-to-Text test →</a></p>
<form method="post">
    <textarea name="text" placeholder="اكتب نص عربي هنا...">{{ text or '' }}</textarea><br>
    <button type="submit">Generate Speech</button>
</form>
{% if audio_file %}
<div class="result">
    <audio controls src="/audio/{{ audio_file }}"></audio>
    <p class="timing">Generated in {{ elapsed }}s</p>
</div>
{% endif %}
</body>
</html>
"""

STT_PAGE = """
<!DOCTYPE html>
<html dir="rtl" lang="ar">
<head><meta charset="UTF-8"><title>Voice Test — Speech to Text</title>
<style>body{font-family:sans-serif;max-width:600px;margin:40px auto;padding:0 20px;}
button{padding:10px 20px;font-size:16px;margin-top:10px;}
.result{margin-top:20px;padding:15px;background:#f0f0f0;border-radius:8px;font-size:18px;}
.timing{color:#666;font-size:13px;}</style>
</head>
<body>
<h1>Voice Test — صوت إلى نص (STT)</h1>
<p><a href="/">Switch to Text-to-Speech test →</a></p>
<form method="post" enctype="multipart/form-data">
    <input type="file" name="audio" accept="audio/*" required><br><br>
    <button type="submit">Transcribe</button>
</form>
{% if transcription %}
<div class="result">
    <strong>Transcribed:</strong> {{ transcription }}
    <p class="timing">Took {{ elapsed }}s</p>
</div>
{% endif %}
</body>
</html>
"""


@app.route("/", methods=["GET", "POST"])
def tts_test():
    audio_file = None
    elapsed = None
    text = None
    if request.method == "POST":
        text = request.form.get("text", "").strip()
        if text:
            t0 = time.time()
            tts = get_tts()
            audio = tts.generate(text, sid=0, speed=1.0)
            filename = f"tts_{int(time.time())}.wav"
            filepath = os.path.join(OUTPUT_DIR, filename)
            with wave.open(filepath, "wb") as f:
                f.setnchannels(1)
                f.setsampwidth(2)
                f.setframerate(audio.sample_rate)
                samples = (np.array(audio.samples) * 32767).astype(np.int16)
                f.writeframes(samples.tobytes())
            audio_file = filename
            elapsed = round(time.time() - t0, 2)
    return render_template_string(TTS_PAGE, audio_file=audio_file, elapsed=elapsed, text=text)


@app.route("/transcribe", methods=["GET", "POST"])
def stt_test():
    transcription = None
    elapsed = None
    if request.method == "POST":
        uploaded = request.files.get("audio")
        if uploaded:
            import soundfile as sf
            temp_path = os.path.join(OUTPUT_DIR, f"upload_{int(time.time())}.wav")
            uploaded.save(temp_path)

            t0 = time.time()
            recognizer = get_stt()
            audio, sample_rate = sf.read(temp_path, dtype="float32")
            stream = recognizer.create_stream()
            stream.accept_waveform(sample_rate, audio)
            recognizer.decode_stream(stream)
            transcription = stream.result.text
            elapsed = round(time.time() - t0, 2)

            os.remove(temp_path)  # don't accumulate uploaded audio
    return render_template_string(STT_PAGE, transcription=transcription, elapsed=elapsed)


@app.route("/audio/<filename>")
def serve_audio(filename):
    # Basic path-safety: only serve files from OUTPUT_DIR, only .wav
    if "/" in filename or ".." in filename or not filename.endswith(".wav"):
        return "Invalid file", 400
    return send_file(os.path.join(OUTPUT_DIR, filename), mimetype="audio/wav")


if __name__ == "__main__":
    # Only used for local testing outside cPanel — cPanel's Passenger
    # setup calls the `app` object directly via passenger_wsgi.py instead.
    app.run(debug=True, port=5000)
