"""AI copy assistant for localization and voiceover script generation."""

from __future__ import annotations

import json
import os
from typing import Any

import anthropic

MODEL_NAME = "claude-3-5-sonnet-20241022"


def _extract_json_object(raw: str) -> dict[str, Any]:
    """Parse a JSON object, tolerating model wrappers around the payload."""
    text = raw.strip()
    if not text:
        raise RuntimeError("Empty AI response")

    try:
        payload = json.loads(text)
    except json.JSONDecodeError:
        start = text.find("{")
        end = text.rfind("}")
        if start < 0 or end <= start:
            raise RuntimeError("AI response was not valid JSON") from None
        payload = json.loads(text[start : end + 1])

    if not isinstance(payload, dict):
        raise RuntimeError("AI response JSON must be an object")
    return payload


class CopyAssistant:
    """Small wrapper around Anthropic for deterministic copy operations."""

    def __init__(self, api_key: str | None = None) -> None:
        resolved_key = (api_key or os.environ.get("ANTHROPIC_API_KEY") or "").strip()
        if not resolved_key:
            raise ValueError("Anthropic API key is required")
        self.client = anthropic.Anthropic(api_key=resolved_key)

    def _request_json(self, prompt: str, max_tokens: int) -> dict[str, Any]:
        response = self.client.messages.create(
            model=MODEL_NAME,
            max_tokens=max_tokens,
            temperature=0.2,
            messages=[{"role": "user", "content": prompt}],
            response_format={"type": "json_object"},
        )

        parts: list[str] = []
        for block in response.content:
            text = getattr(block, "text", None)
            if isinstance(text, str):
                parts.append(text)

        raw = "".join(parts).strip()
        return _extract_json_object(raw)

    def localize_text(
        self,
        *,
        text: str,
        target_language: str,
        source_language: str | None = None,
    ) -> str:
        source_hint = (
            f"Source language hint: {source_language.strip()}.\n"
            if source_language and source_language.strip()
            else ""
        )

        prompt = (
            "You localize software walkthrough copy.\n"
            "Return ONLY valid JSON in this shape: {\"text\":\"...\"}.\n"
            "Rules:\n"
            "- Translate into the requested target language.\n"
            "- Keep placeholders exactly (for example {{name}}).\n"
            "- Keep commands, file paths, and code spans unchanged when possible.\n"
            "- Keep the output concise, instructional, and natural.\n"
            f"{source_hint}"
            f"Target language: {target_language.strip()}.\n"
            f"Text to localize:\n{text.strip()}\n"
        )

        payload = self._request_json(prompt, max_tokens=1200)
        localized = payload.get("text")
        if not isinstance(localized, str) or not localized.strip():
            raise RuntimeError("Localization response did not include text")
        return localized.strip()

    def generate_voiceover_script(
        self,
        *,
        text: str,
        language: str | None = None,
        tone: str | None = None,
        max_seconds: int = 12,
    ) -> str:
        language_hint = (
            f"Narration language: {language.strip()}.\n"
            if language and language.strip()
            else "Narration language: match the input language.\n"
        )
        tone_hint = (
            f"Voice tone: {tone.strip()}.\n"
            if tone and tone.strip()
            else "Voice tone: confident, concise, and clear.\n"
        )

        prompt = (
            "Write a voiceover script for a product demo step or hotspot.\n"
            "Return ONLY valid JSON in this shape: {\"script\":\"...\"}.\n"
            "Rules:\n"
            "- Keep it short and natural for spoken delivery.\n"
            "- Use one to two sentences.\n"
            "- Keep placeholders exactly (for example {{name}}).\n"
            "- Keep commands, shortcuts, and code snippets unchanged.\n"
            f"- Target spoken duration under {max_seconds} seconds.\n"
            f"{language_hint}"
            f"{tone_hint}"
            f"Source text:\n{text.strip()}\n"
        )

        payload = self._request_json(prompt, max_tokens=700)
        script = payload.get("script")
        if not isinstance(script, str) or not script.strip():
            raise RuntimeError("Voiceover response did not include script")
        return script.strip()
