from dataclasses import dataclass

from django.conf import settings
from openai import OpenAI


@dataclass(frozen=True)
class OpenAIChatResult:
    content: str
    model: str
    input_tokens: int | None = None
    output_tokens: int | None = None


class OpenAIChatService:
    def __init__(self):
        self.client = OpenAI(
            api_key=settings.OPENAI_API_KEY,
            timeout=settings.OPENAI_TIMEOUT,
        )

    def generate_reply(self, *, messages, options=None):
        options = options or {}
        model = settings.OPENAI_MODEL
        max_tokens = options.get("max_tokens", settings.OPENAI_MAX_OUTPUT_TOKENS)
        temperature = options.get("temperature")

        if model.startswith("gpt-5"):
            # GPT-5: prioriza resposta curta/final e reduz gasto com reasoning.
            req = {
                "model": model,
                "input": messages,
                "max_output_tokens": max_tokens,
                "reasoning": {"effort": "low"},
                "text": {"verbosity": "low"},
            }
            if temperature is not None:
                req["temperature"] = temperature
            response = self.client.responses.create(
                **req,
            )
            reply = self._extract_reply_text_from_responses(response)
            usage = getattr(response, "usage", None)
            return OpenAIChatResult(
                content=reply.strip(),
                model=getattr(response, "model", model),
                input_tokens=getattr(usage, "input_tokens", None),
                output_tokens=getattr(usage, "output_tokens", None),
            )

        response = self.client.chat.completions.create(
            model=model,
            messages=messages,
            max_completion_tokens=max_tokens,
            temperature=temperature,
        )
        reply = self._extract_reply_text(response)
        usage = getattr(response, "usage", None)
        return OpenAIChatResult(
            content=reply.strip(),
            model=response.model,
            input_tokens=getattr(usage, "prompt_tokens", None),
            output_tokens=getattr(usage, "completion_tokens", None),
        )

    def _extract_reply_text(self, response):
        """
        Compatibilidade defensiva com variações de payload do SDK/modelo.
        """
        if not getattr(response, "choices", None):
            return ""
        message = getattr(response.choices[0], "message", None)
        if message is None:
            return ""

        content = getattr(message, "content", "")
        if isinstance(content, str):
            if content.strip():
                return content
        elif isinstance(content, list):
            # Alguns modelos retornam lista de partes (text/refusal/etc).
            parts = []
            for part in content:
                if isinstance(part, str):
                    if part.strip():
                        parts.append(part)
                    continue
                if isinstance(part, dict):
                    text = part.get("text")
                    if isinstance(text, str) and text.strip():
                        parts.append(text)
            if parts:
                return "\n".join(parts)

        refusal = getattr(message, "refusal", None)
        if isinstance(refusal, str) and refusal.strip():
            return refusal

        return ""

    def _extract_reply_text_from_responses(self, response):
        text = getattr(response, "output_text", None)
        if isinstance(text, str) and text.strip():
            return text

        # Fallback defensivo caso output_text não venha preenchido.
        output = getattr(response, "output", None)
        parts = self._extract_text_parts_from_output(output)
        if parts:
            return "\n".join(parts)

        # Último fallback: serializa para dict e tenta extrair de lá.
        model_dump = getattr(response, "model_dump", None)
        if callable(model_dump):
            as_dict = model_dump()
            parts = self._extract_text_parts_from_output(as_dict.get("output"))
            if parts:
                return "\n".join(parts)

        return ""

    def _extract_text_parts_from_output(self, output):
        if not isinstance(output, list):
            return []
        parts = []
        for item in output:
            content = self._read_field(item, "content")
            if not isinstance(content, list):
                continue
            for part in content:
                ptext = self._read_field(part, "text")
                if isinstance(ptext, str) and ptext.strip():
                    parts.append(ptext)
                    continue
                # Alguns payloads encapsulam texto em output_text.
                alt = self._read_field(part, "output_text")
                if isinstance(alt, str) and alt.strip():
                    parts.append(alt)
        return parts

    def _read_field(self, obj, name):
        if isinstance(obj, dict):
            return obj.get(name)
        return getattr(obj, name, None)
