"""Python 3.9+, standard library only. Set FLOWSPEECH_API_KEY before running."""
# Keep the downloadable copy in public/examples/public-api/generate.py in sync.
import json
import os
import time
import uuid
from http.client import HTTPException
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

KEY = os.environ["FLOWSPEECH_API_KEY"]
BASE = os.environ.get("FLOWSPEECH_BASE_URL", "https://flowspeech.io").rstrip("/")
REQUEST_ID = os.environ.get("FLOWSPEECH_REQUEST_ID") or str(uuid.uuid4())
print(f"Recovery requestId: {REQUEST_ID}")


def api(path, body=None):
    for attempt in range(5):
        request = Request(BASE + path, data=json.dumps(body).encode() if body else None, headers={
            "Authorization": f"Bearer {KEY}", "Content-Type": "application/json", "Idempotency-Key": REQUEST_ID,
        })
        try:
            with urlopen(request, timeout=30) as response:
                return json.load(response)
        except HTTPError as error:
            if error.code not in (429, 500, 502, 503, 504) or attempt == 4:
                raise RuntimeError(f"HTTP {error.code}; recover with requestId {REQUEST_ID}") from error
            delay = int(error.headers.get("Retry-After", 2 ** attempt))
            if delay > 60:
                raise RuntimeError(f"Retry after {delay} seconds using requestId {REQUEST_ID}") from error
            time.sleep(delay)
        except (URLError, TimeoutError, ConnectionError, HTTPException,
                json.JSONDecodeError, UnicodeDecodeError):
            if attempt == 4:
                raise
            time.sleep(2 ** attempt)


job = api("/api/v1/tts/jobs", {"input": {"mode": "single", "text": "Hello world.", "voiceName": "Kore"}})
deadline = time.monotonic() + 2 * 60 * 60
while job["status"] not in ("success", "failed", "cancelled"):
    if time.monotonic() > deadline:
        raise RuntimeError(f"Polling stopped. Check job {job['id']}; stopping this script does not cancel it.")
    time.sleep(5)
    job = api(f"/api/v1/tts/jobs/{job['id']}")
if job["status"] != "success" or not job.get("result"):
    raise RuntimeError(f"Job {job['id']}: {job['status']} {job.get('error', {})}")
# Do not send your API key to the storage host.
with urlopen(job["result"]["url"], timeout=120) as audio:
    with Path("speech.wav").open("wb") as output:
        while chunk := audio.read(1024 * 1024):
            output.write(chunk)
print(f"Saved speech.wav; charged {job['usage']['chargedCredits']} credits.")
