"""Servico de integracao com LLM via OpenRouter."""

import asyncio
import logging
import os
import time
from typing import Any, ClassVar

from openrouter import OpenRouter
from openrouter.errors.badgatewayresponse_error import BadGatewayResponseError
from openrouter.errors.internalserverresponse_error import InternalServerResponseError
from openrouter.errors.provideroverloadedresponse_error import (
    ProviderOverloadedResponseError,
)
from openrouter.errors.requesttimeoutresponse_error import RequestTimeoutResponseError
from openrouter.errors.serviceunavailableresponse_error import (
    ServiceUnavailableResponseError,
)
from openrouter.errors.toomanyrequestsresponse_error import (
    TooManyRequestsResponseError,
)
from pydantic import BaseModel
from tenacity import (
    before_sleep_log,
    retry,
    retry_if_exception_type,
    stop_after_attempt,
    wait_exponential,
)

from services.usage_logger import get_usage_logger, ms_since

logger = logging.getLogger("tecjustica-mcp.llm")

# Erros retryable do OpenRouter (429, 5xx, timeout, provider overload)
_RETRYABLE = (
    TooManyRequestsResponseError,
    InternalServerResponseError,
    BadGatewayResponseError,
    ServiceUnavailableResponseError,
    ProviderOverloadedResponseError,
    RequestTimeoutResponseError,
)

# Timeout para cada chamada LLM (segundos)
LLM_CALL_TIMEOUT = 120.0


@retry(
    retry=retry_if_exception_type(_RETRYABLE),
    stop=stop_after_attempt(4),
    wait=wait_exponential(multiplier=2, min=2, max=60),
    before_sleep=before_sleep_log(logger, logging.WARNING),
    reraise=True,
)
async def _send_with_retry(client: OpenRouter, **kwargs: Any) -> Any:
    """Envia request ao OpenRouter com retry e timeout."""
    return await asyncio.wait_for(
        asyncio.to_thread(lambda: client.chat.send(**kwargs)),
        timeout=LLM_CALL_TIMEOUT,
    )


class LLMResponse(BaseModel):
    """Resposta do LLM."""

    text: str
    model: str
    tokens_prompt: int | None = None
    tokens_completion: int | None = None
    tokens_total: int | None = None


class LLMService:
    """Servico para queries complexas usando LLM via OpenRouter."""

    # Modelo unico: Gemini Flash (1M contexto, $0.15/1M input)
    MODEL_ID: ClassVar[str] = "google/gemini-2.5-flash"
    MODEL_ALIAS: ClassVar[str] = "gemini-flash"

    # Compat: manter MODELS para codigo legado
    MODELS: ClassVar[dict[str, str]] = {"gemini-flash": MODEL_ID}
    MODELS_REVERSE: ClassVar[dict[str, str]] = {MODEL_ID: MODEL_ALIAS}

    SYSTEM_PROMPT = """Voce e um assistente juridico especializado em analise de processos judiciais brasileiros.

Sua funcao e responder perguntas sobre processos judiciais com base no contexto fornecido.

Diretrizes:
1. Sempre baseie suas respostas APENAS no contexto fornecido
2. Se a informacao nao estiver no contexto, diga claramente que nao ha dados disponiveis
3. Use linguagem clara e objetiva
4. Cite datas, valores e nomes quando relevantes
5. Para valores monetarios, use formato brasileiro (R$ X.XXX,XX)
6. Mantenha a confidencialidade - nao invente dados
7. Se perguntado sobre prazos, mencione que e importante verificar com advogado

Formato de resposta:
- Seja direto e objetivo
- Use bullet points quando apropriado
- Destaque informacoes importantes
"""

    def __init__(
        self,
        api_key: str | None = None,
        default_model: str = MODEL_ALIAS,
    ):
        self.api_key = api_key or os.getenv("OPENROUTER_API_KEY")
        self.default_model = default_model

        if not self.api_key:
            raise ValueError("OPENROUTER_API_KEY is required")

        self._client = OpenRouter(api_key=self.api_key)

    async def query(
        self,
        pergunta: str,
        contexto: str,
        model: str | None = None,  # noqa: ARG002 — mantido para compat
        temperature: float = 0.3,
        max_tokens: int = 2000,
        tool_name: str | None = None,
        user_id: str | None = None,
        user_email: str | None = None,
    ) -> LLMResponse:
        """
        Faz uma query ao LLM com contexto do processo (sempre Gemini Flash).

        Args:
            pergunta: Pergunta do usuario
            contexto: Contexto do processo (texto formatado)
            model: Ignorado (mantido para compat). Sempre usa Gemini Flash.
            temperature: Temperatura para geracao
            max_tokens: Maximo de tokens na resposta
            tool_name: Nome da tool que originou a chamada (para logging)
            user_id: ID do usuario (para logging)
            user_email: Email do usuario (para logging)

        Returns:
            LLMResponse com a resposta
        """
        model_id = self.MODEL_ID
        _start = time.perf_counter()

        # Monta o prompt
        user_message = f"""CONTEXTO DO PROCESSO:

{contexto}

---

PERGUNTA DO USUARIO:
{pergunta}

Por favor, responda a pergunta acima com base apenas no contexto fornecido."""

        # Faz a requisicao com retry + timeout
        response = await _send_with_retry(
            self._client,
            model=model_id,
            messages=[
                {"role": "system", "content": self.SYSTEM_PROMPT},
                {"role": "user", "content": user_message},
            ],
            temperature=temperature,
            max_tokens=max_tokens,
            stream=False,
        )

        duration = ms_since(_start)

        # Extrai resposta
        text = ""
        if response.choices and len(response.choices) > 0:
            text = response.choices[0].message.content or ""

        # Extrai tokens (OpenRouter retorna floats — cast para int)
        tokens_prompt = None
        tokens_completion = None
        tokens_total = None
        if response.usage:
            tokens_prompt = (
                int(response.usage.prompt_tokens)
                if response.usage.prompt_tokens is not None
                else None
            )
            tokens_completion = (
                int(response.usage.completion_tokens)
                if response.usage.completion_tokens is not None
                else None
            )
            tokens_total = (
                int(response.usage.total_tokens)
                if response.usage.total_tokens is not None
                else None
            )

        # Log usage
        try:
            get_usage_logger().log_llm_call(
                tool_name=tool_name or "unknown",
                call_type="query",
                model_id=model_id,
                model_alias=self.MODELS_REVERSE.get(model_id),
                tokens_prompt=tokens_prompt,
                tokens_completion=tokens_completion,
                tokens_total=tokens_total,
                context_chars=len(contexto),
                duration_ms=duration,
                user_id=user_id,
                user_email=user_email,
            )
        except Exception:
            pass

        return LLMResponse(
            text=text,
            model=model_id,
            tokens_prompt=tokens_prompt,
            tokens_completion=tokens_completion,
            tokens_total=tokens_total,
        )

    async def verificar_contexto(
        self,
        contexto: str,
        max_chars: int = 500000,
    ) -> dict[str, Any]:
        """
        Verifica se o contexto cabe no modelo.

        Args:
            contexto: Texto do contexto
            max_chars: Limite de caracteres

        Returns:
            Dict com informacoes sobre o contexto
        """
        chars = len(contexto)
        tokens_estimados = chars // 4  # Estimativa: 1 token ≈ 4 chars

        return {
            "chars": chars,
            "tokens_estimados": tokens_estimados,
            "cabe_no_modelo": chars <= max_chars,
            "modelo_recomendado": "gemini-flash",
        }

    async def resumir_texto(
        self,
        texto: str,
        max_tokens_saida: int = 4000,
        instrucao: str | None = None,
        model: str | None = None,  # noqa: ARG002 — mantido para compat
        tool_name: str | None = None,
        user_id: str | None = None,
        user_email: str | None = None,
    ) -> LLMResponse:
        """
        Resume um texto longo com Gemini Flash (1M contexto).

        Args:
            texto: Texto a ser resumido
            max_tokens_saida: Tamanho maximo do resumo em tokens
            instrucao: Instrucao especifica para o resumo (opcional)
            model: Modelo a usar (default: gemini-flash)
            tool_name: Nome da tool que originou a chamada (para logging)
            user_id: ID do usuario (para logging)
            user_email: Email do usuario (para logging)

        Returns:
            LLMResponse com o resumo
        """
        model_id = self.MODEL_ID
        _start = time.perf_counter()

        instrucao_default = """Resuma este documento juridico de forma concisa, mantendo:
1. Informacoes essenciais (datas, valores, partes, decisoes)
2. Principais argumentos e fundamentacao
3. Pedidos e conclusoes
4. Prazos relevantes

Seja objetivo e mantenha a precisao tecnica juridica."""

        prompt = f"""{instrucao or instrucao_default}

DOCUMENTO ORIGINAL:
{texto}

RESUMO:"""

        response = await _send_with_retry(
            self._client,
            model=model_id,
            messages=[
                {
                    "role": "system",
                    "content": "Voce e um assistente especializado em resumir documentos juridicos brasileiros de forma precisa e concisa.",
                },
                {"role": "user", "content": prompt},
            ],
            temperature=0.2,
            max_tokens=max_tokens_saida,
            stream=False,
        )

        duration = ms_since(_start)

        text = ""
        if response.choices and len(response.choices) > 0:
            text = response.choices[0].message.content or ""

        tokens_prompt = None
        tokens_completion = None
        tokens_total = None
        if response.usage:
            tokens_prompt = (
                int(response.usage.prompt_tokens)
                if response.usage.prompt_tokens is not None
                else None
            )
            tokens_completion = (
                int(response.usage.completion_tokens)
                if response.usage.completion_tokens is not None
                else None
            )
            tokens_total = (
                int(response.usage.total_tokens)
                if response.usage.total_tokens is not None
                else None
            )

        # Log usage
        try:
            get_usage_logger().log_llm_call(
                tool_name=tool_name or "unknown",
                call_type="resumo",
                model_id=model_id,
                model_alias=self.MODELS_REVERSE.get(model_id),
                tokens_prompt=tokens_prompt,
                tokens_completion=tokens_completion,
                tokens_total=tokens_total,
                context_chars=len(texto),
                duration_ms=duration,
                user_id=user_id,
                user_email=user_email,
            )
        except Exception:
            pass

        return LLMResponse(
            text=text,
            model=model_id,
            tokens_prompt=tokens_prompt,
            tokens_completion=tokens_completion,
            tokens_total=tokens_total,
        )

    async def processar_processo_para_claude(
        self,
        dados_processo: dict[str, Any],
        pergunta: str | None = None,
    ) -> str:
        """
        Pre-processa dados de processo usando Gemini (1M contexto)
        e retorna resumo compacto para Claude.

        Args:
            dados_processo: Dict com dados brutos do processo
            pergunta: Pergunta especifica do usuario (opcional)

        Returns:
            Resumo estruturado e compacto (max ~2000 chars)
        """
        import json

        # Serializa dados para texto
        dados_texto = json.dumps(dados_processo, ensure_ascii=False, indent=2, default=str)

        instrucao = """Analise os dados deste processo judicial e gere um RESUMO ESTRUTURADO e COMPACTO.

FORMATO OBRIGATORIO (use exatamente este formato):

**PROCESSO**: [numero]
**CLASSE**: [classe processual]
**TRIBUNAL**: [tribunal] | **INSTANCIA**: [instancia]
**VALOR**: R$ [valor]

**PARTES**:
- Autor(es): [nomes, max 3]
- Reu(s): [nomes, max 3]

**ASSUNTOS**: [assuntos principais, max 3]

**STATUS**: [situacao atual do processo]

**RESUMO**: [2-3 frases sobre o que trata o processo]"""

        if pergunta:
            instrucao += f"""

**RESPOSTA A PERGUNTA**: "{pergunta}"
[Responda de forma direta e objetiva]"""

        instrucao += """

REGRAS:
- Maximo 1500 caracteres no total
- Seja EXTREMAMENTE conciso
- Use apenas informacoes presentes nos dados
- Nao invente informacoes"""

        response = await self.resumir_texto(
            texto=dados_texto,
            max_tokens_saida=800,
            instrucao=instrucao,
            model="gemini-flash",
        )

        return response.text

    async def processar_lista_para_claude(
        self,
        dados_lista: list[dict[str, Any]],
        tipo: str = "processos",
    ) -> str:
        """
        Pre-processa lista de itens usando Gemini.

        Args:
            dados_lista: Lista de dicts
            tipo: Tipo de dados (processos, documentos, movimentacoes)

        Returns:
            Resumo compacto da lista
        """
        import json

        dados_texto = json.dumps(dados_lista, ensure_ascii=False, indent=2, default=str)

        instrucoes = {
            "processos": """Liste os processos de forma COMPACTA:
Para cada processo, uma linha: [numero] | [classe] | [tribunal] | R$ [valor]
Max 10 processos. Total no final.""",
            "documentos": """Liste os documentos de forma COMPACTA:
Para cada doc: [tipo] | [data] | [paginas]p
Agrupe por tipo se muitos. Max 15 docs.""",
            "movimentacoes": """Liste movimentacoes de forma COMPACTA:
Para cada mov: [data] - [descricao curta]
Max 10 movimentacoes mais recentes.""",
        }

        instrucao = instrucoes.get(tipo, instrucoes["processos"])
        instrucao += "\n\nMAX 1000 caracteres. Seja MUITO conciso."

        response = await self.resumir_texto(
            texto=dados_texto,
            max_tokens_saida=500,
            instrucao=instrucao,
            model="gemini-flash",
        )

        return response.text

    def listar_modelos(self) -> dict[str, str]:
        """Lista modelos disponiveis."""
        return self.MODELS.copy()


_instance: LLMService | None = None


def get_llm_service() -> LLMService:
    """Factory singleton para servico LLM."""
    global _instance
    if _instance is None:
        _instance = LLMService()
    return _instance
