from __future__ import annotations

import json
import re
from typing import Any

import anthropic
from tenacity import retry, stop_after_attempt, wait_exponential


class ClaudeService:
    """Thin wrapper around the Anthropic Messages API.

    Used for:
      - Ideation (ideas batch JSON)
      - Asset copy / slide scripts / video narration briefs
    Image pixels themselves are rendered locally (Pillow); Claude supplies copy + layout.
    """

    def __init__(self, api_key: str, model: str):
        self.client = anthropic.Anthropic(api_key=api_key)
        self.model = model

    @retry(stop=stop_after_attempt(3), wait=wait_exponential(min=2, max=20), reraise=True)
    def complete(self, system: str, user: str, max_tokens: int = 4096) -> str:
        msg = self.client.messages.create(
            model=self.model,
            max_tokens=max_tokens,
            system=system,
            messages=[{"role": "user", "content": user}],
        )
        parts = [b.text for b in msg.content if getattr(b, "type", None) == "text"]
        return "\n".join(parts).strip()

    def complete_json(self, system: str, user: str, max_tokens: int = 4096) -> Any:
        raw = self.complete(
            system=system + "\n\nRespond with valid JSON only. No markdown fences.",
            user=user,
            max_tokens=max_tokens,
        )
        return parse_json_loose(raw)


def parse_json_loose(text: str) -> Any:
    text = text.strip()
    fence = re.search(r"```(?:json)?\s*([\s\S]*?)```", text)
    if fence:
        text = fence.group(1).strip()
    try:
        return json.loads(text)
    except json.JSONDecodeError:
        start = text.find("{")
        end = text.rfind("}")
        if start >= 0 and end > start:
            return json.loads(text[start : end + 1])
        start = text.find("[")
        end = text.rfind("]")
        if start >= 0 and end > start:
            return json.loads(text[start : end + 1])
        raise
