"""Tools de busca — grep, glob, localizar."""

import logging
import time
from typing import Any, Literal

from fastmcp import FastMCP

from services.supabase_client import get_supabase_client
from services.usage_logger import get_usage_logger, ms_since
from tools._helpers import (
    _get_current_user,
    erro_instrutivo,
    paginar_resultado,
)

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


def register_busca_tools(mcp: FastMCP) -> None:
    """Registra tools de busca."""

    @mcp.tool(annotations={"readOnlyHint": True, "openWorldHint": False})
    async def grep_documentos(
        padrao: str,
        modo: Literal["fulltext", "regex", "ilike"] = "fulltext",
        numero_processo: str | None = None,
        tipo_nome: str | None = None,
        fonte: str | None = None,
        limite: int = 20,
        offset: int = 0,
    ) -> dict[str, Any]:
        """
        Busca conteudo nos documentos usando full-text search, regex ou ilike. Retorna snippets com highlight.

        QUANDO USAR:
        - Encontrar documentos que mencionam um termo ("sentenca", "dano moral", "honorarios")
        - Busca semantica em linguagem natural (modo fulltext, default)
        - Busca por padrao regex para termos exatos ou expressoes regulares
        - Busca case-insensitive simples (modo ilike)

        QUANDO NAO USAR:
        - Quer listar documentos sem buscar conteudo → use ls_documentos()
        - Quer ler o conteudo completo → use read_documento()
        - Quer filtrar por tipo/nome sem buscar conteudo → use glob_documentos()

        Args:
            padrao: Termo de busca. No modo fulltext, aceita linguagem natural ("dano moral indenizacao").
                    No modo regex, aceita expressao regular ("sentenc[a|ç]a").
                    No modo ilike, busca case-insensitive.
            modo: "fulltext" (default, usa indice FTS — rapido), "regex" (expressao regular), "ilike" (case-insensitive).
            numero_processo: Filtrar por processo especifico (opcional).
            tipo_nome: Filtrar por tipo de documento, ex: "Sentenca", "Peticao" (opcional, parcial).
            fonte: Filtrar por origem dos dados: "api" ou "segmentation" (opcional, default=ambas).
            limite: Maximo de resultados (default 20).
            offset: Pular N resultados para paginacao.

        Returns:
            Lista de matches com id, tipo, nome, relevancia, snippet (com >>>highlight<<<), fonte e paginacao.
        """
        _start = time.perf_counter()
        user_id, user_email = _get_current_user()

        try:
            sb = get_supabase_client().client

            response = sb.rpc(
                "buscar_documentos",
                {
                    "p_termo": padrao,
                    "p_modo": modo,
                    "p_numero_processo": numero_processo,
                    "p_tipo_nome": tipo_nome,
                    "p_source": fonte,
                    "p_limite": limite,
                    "p_offset": offset,
                },
            ).execute()

            dados = response.data or []
            total = int(dados[0].get("total_count") or 0) if dados else 0

            matches = [
                {
                    "id": d.get("id"),
                    "id_origem": d.get("id_origem"),
                    "numero_processo": d.get("numero_processo"),
                    "tipo_nome": d.get("tipo_nome"),
                    "nome": d.get("nome"),
                    "sequencia": d.get("sequencia"),
                    "data_juntada": d.get("data_juntada"),
                    "nivel_sigilo": d.get("nivel_sigilo"),
                    "tamanho_texto": d.get("tamanho_texto"),
                    "paginas": d.get("paginas"),
                    "relevancia": d.get("relevancia"),
                    "snippet": d.get("snippet"),
                    "fonte": d.get("source"),
                    "confianca": d.get("confianca"),
                }
                for d in dados
            ]

            resultado = paginar_resultado(matches, total, limite, offset)
            resultado["dica"] = (
                "Use read_documento(id) para ler o conteudo completo, "
                "ou localizar_no_documento(id, termo) para ver todas as posicoes do termo no documento."
            )

            try:
                get_usage_logger().log_tool_call(
                    tool_name="grep_documentos",
                    duration_ms=ms_since(_start),
                    user_id=user_id,
                    user_email=user_email,
                    params={
                        "padrao": padrao,
                        "modo": modo,
                        "numero_processo": numero_processo,
                        "tipo_nome": tipo_nome,
                        "fonte": fonte,
                        "limite": limite,
                        "offset": offset,
                    },
                )
            except Exception:
                pass
            return resultado
        except Exception as e:
            try:
                get_usage_logger().log_tool_call(
                    tool_name="grep_documentos",
                    duration_ms=ms_since(_start),
                    user_id=user_id,
                    user_email=user_email,
                    success=False,
                    error_message=str(e),
                )
            except Exception:
                pass
            return erro_instrutivo(
                str(e),
                "Verifique o padrao de busca. No modo regex, escape caracteres especiais.",
            )

    @mcp.tool(annotations={"readOnlyHint": True, "openWorldHint": False})
    async def grep_movimentacoes(
        padrao: str,
        numero_processo: str | None = None,
        tipo_nome: str | None = None,
        fonte: str | None = None,
        limite: int = 30,
        offset: int = 0,
    ) -> dict[str, Any]:
        """
        Busca full-text nas movimentacoes. Retorna snippets com highlight.

        QUANDO USAR:
        - Encontrar movimentacoes que mencionam um termo ("audiencia", "sentenca", "citacao")
        - Buscar decisoes, despachos ou eventos especificos no historico

        QUANDO NAO USAR:
        - Quer ver timeline completa → use ls_movimentacoes()
        - Quer buscar em documentos → use grep_documentos()

        Args:
            padrao: Termo de busca em linguagem natural (usa full-text search).
            numero_processo: Filtrar por processo especifico (opcional).
            tipo_nome: Filtrar por tipo de movimentacao (opcional, parcial).
            fonte: Filtrar por origem dos dados: "api" ou "segmentation" (opcional, default=ambas).
            limite: Maximo de resultados (default 30).
            offset: Pular N resultados para paginacao.

        Returns:
            Lista de movimentacoes encontradas com data, descricao, snippet, fonte e paginacao.
        """
        _start = time.perf_counter()
        user_id, user_email = _get_current_user()

        try:
            sb = get_supabase_client().client

            response = sb.rpc(
                "buscar_movimentacoes",
                {
                    "p_termo": padrao,
                    "p_numero_processo": numero_processo,
                    "p_tipo_nome": tipo_nome,
                    "p_source": fonte,
                    "p_limite": limite,
                    "p_offset": offset,
                },
            ).execute()

            dados = response.data or []
            total = int(dados[0].get("total_count") or 0) if dados else 0

            matches = []
            for d in dados:
                desc = d.get("descricao") or ""
                if len(desc) > 300:
                    desc = desc[:300] + "..."
                matches.append(
                    {
                        "id": d.get("id"),
                        "numero_processo": d.get("numero_processo"),
                        "sequencia": d.get("sequencia"),
                        "data_hora": d.get("data_hora"),
                        "tipo_nome": d.get("tipo_nome"),
                        "descricao": desc,
                        "magistrado_nome": d.get("magistrado_nome"),
                        "orgao_julgador_nome": d.get("orgao_julgador_nome"),
                        "snippet": d.get("snippet"),
                        "fonte": d.get("source"),
                    }
                )

            resultado = paginar_resultado(matches, total, limite, offset)

            try:
                get_usage_logger().log_tool_call(
                    tool_name="grep_movimentacoes",
                    duration_ms=ms_since(_start),
                    user_id=user_id,
                    user_email=user_email,
                    params={
                        "padrao": padrao,
                        "numero_processo": numero_processo,
                        "tipo_nome": tipo_nome,
                        "fonte": fonte,
                        "limite": limite,
                        "offset": offset,
                    },
                )
            except Exception:
                pass
            return resultado
        except Exception as e:
            try:
                get_usage_logger().log_tool_call(
                    tool_name="grep_movimentacoes",
                    duration_ms=ms_since(_start),
                    user_id=user_id,
                    user_email=user_email,
                    success=False,
                    error_message=str(e),
                )
            except Exception:
                pass
            return erro_instrutivo(
                str(e),
                "Verifique o padrao de busca.",
            )

    @mcp.tool(annotations={"readOnlyHint": True, "openWorldHint": False})
    async def glob_documentos(
        numero_processo: str,
        padrao_nome: str | None = None,
        padrao_tipo: str | None = None,
        nivel_sigilo: str | None = None,
        fonte: str | None = None,
        limite: int = 50,
        offset: int = 0,
    ) -> dict[str, Any]:
        """
        Filtra documentos por nome, tipo ou sigilo SEM buscar no conteudo.

        QUANDO USAR:
        - Encontrar documentos por tipo: glob_documentos(cnj, padrao_tipo="Sentenca")
        - Filtrar por nome: glob_documentos(cnj, padrao_nome="peticao inicial")
        - Filtrar por sigilo: glob_documentos(cnj, nivel_sigilo="SEGREDO_DE_JUSTICA")
        - Combinar filtros: glob_documentos(cnj, padrao_tipo="Decisao", nivel_sigilo="PUBLICO")
        - Filtrar por fonte: glob_documentos(cnj, fonte="segmentation")

        QUANDO NAO USAR:
        - Quer buscar DENTRO do conteudo → use grep_documentos()
        - Quer listar TODOS os documentos → use ls_documentos()
        - Quer ler conteudo → use read_documento()

        Args:
            numero_processo: Numero CNJ do processo.
            padrao_nome: Filtrar por nome do documento (case-insensitive, parcial). Ex: "peticao", "sentenca".
            padrao_tipo: Filtrar por tipo do documento (case-insensitive, parcial). Ex: "Sentenca", "Despacho".
            nivel_sigilo: Filtrar por nivel de sigilo exato. Ex: "PUBLICO", "SEGREDO_DE_JUSTICA".
            fonte: Filtrar por origem dos dados: "api" ou "segmentation" (opcional, default=ambas).
            limite: Maximo de resultados (default 50, max 100).
            offset: Pular N resultados para paginacao.

        Returns:
            Lista de documentos filtrados com metadados (sem conteudo), fonte, confianca e paginacao.
        """
        _start = time.perf_counter()
        user_id, user_email = _get_current_user()
        limite = min(limite, 100)

        try:
            sb = get_supabase_client().client

            query = (
                sb.table("documents")
                .select(
                    "id, id_origem, numero_processo, tipo_nome, nome, sequencia, data_juntada, "
                    "tamanho_texto, tamanho_bytes, paginas, nivel_sigilo, metodo_extracao, "
                    "signatarios, tipo_arquivo, source, confianca",
                    count="exact",
                )
                .eq("numero_processo", numero_processo)
            )

            if padrao_nome:
                query = query.ilike("nome", f"%{padrao_nome}%")
            if padrao_tipo:
                query = query.ilike("tipo_nome", f"%{padrao_tipo}%")
            if nivel_sigilo:
                query = query.eq("nivel_sigilo", nivel_sigilo)
            if fonte:
                query = query.eq("source", fonte)

            query = query.order("sequencia", desc=True).range(offset, offset + limite - 1)

            response = query.execute()
            total = response.count or 0
            documentos = response.data or []

            resultado = paginar_resultado(documentos, total, limite, offset)
            resultado["dica"] = (
                "Use read_documento(id) para ler conteudo, "
                "grep_documentos(padrao) para buscar dentro do conteudo."
            )

            try:
                get_usage_logger().log_tool_call(
                    tool_name="glob_documentos",
                    duration_ms=ms_since(_start),
                    user_id=user_id,
                    user_email=user_email,
                    params={
                        "numero_processo": numero_processo,
                        "padrao_nome": padrao_nome,
                        "padrao_tipo": padrao_tipo,
                        "nivel_sigilo": nivel_sigilo,
                        "fonte": fonte,
                        "limite": limite,
                        "offset": offset,
                    },
                )
            except Exception:
                pass
            return resultado
        except Exception as e:
            try:
                get_usage_logger().log_tool_call(
                    tool_name="glob_documentos",
                    duration_ms=ms_since(_start),
                    user_id=user_id,
                    user_email=user_email,
                    success=False,
                    error_message=str(e),
                )
            except Exception:
                pass
            return erro_instrutivo(str(e), "Verifique o numero do processo e os filtros.")

    @mcp.tool(annotations={"readOnlyHint": True, "openWorldHint": False})
    async def localizar_no_documento(
        document_id: str,
        termo: str,
        contexto_chars: int = 100,
        max_ocorrencias: int = 30,
    ) -> dict[str, Any]:
        """
        Localiza todas as ocorrencias de um termo em um documento, com posicoes e snippets.

        QUANDO USAR:
        - Saber ONDE exatamente um termo aparece em um documento longo
        - Ponte entre grep_documentos() (que doc?) e read_documento() (onde ler?)
        - Exemplo: grep achou "dano moral" no doc X → localizar mostra posicoes → read com offset preciso

        QUANDO NAO USAR:
        - Quer buscar em VARIOS documentos → use grep_documentos()
        - Quer ler o documento inteiro → use read_documento()
        - Nao sabe em qual documento buscar → use grep_documentos() primeiro

        Args:
            document_id: UUID do documento.
            termo: Termo a localizar (case-insensitive).
            contexto_chars: Caracteres de contexto ao redor de cada ocorrencia (default 100).
            max_ocorrencias: Maximo de ocorrencias a retornar (default 30).

        Returns:
            Posicoes e snippets de cada ocorrencia, total de ocorrencias e tamanho do documento.
        """
        _start = time.perf_counter()
        user_id, user_email = _get_current_user()

        try:
            sb = get_supabase_client().client

            response = sb.rpc(
                "localizar_no_documento",
                {
                    "p_document_id": document_id,
                    "p_termo": termo,
                    "p_contexto_chars": contexto_chars,
                    "p_max_ocorrencias": max_ocorrencias,
                },
            ).execute()

            dados = response.data
            if isinstance(dados, list) and dados:
                dados = dados[0]

            if not dados:
                try:
                    get_usage_logger().log_tool_call(
                        tool_name="localizar_no_documento",
                        duration_ms=ms_since(_start),
                        user_id=user_id,
                        user_email=user_email,
                        success=False,
                        error_message="documento_nao_encontrado_ou_vazio",
                        params={"document_id": document_id, "termo": termo},
                    )
                except Exception:
                    pass
                return erro_instrutivo(
                    "Documento nao encontrado ou sem conteudo.",
                    "Verifique o document_id com ls_documentos().",
                )

            resultado: dict[str, Any] = {
                "document_id": dados.get("document_id"),
                "termo": dados.get("termo"),
                "fonte": dados.get("fonte"),
                "confianca": dados.get("confianca"),
                "total_ocorrencias": dados.get("total_ocorrencias"),
                "tamanho_documento": dados.get("tamanho_documento"),
                "ocorrencias": dados.get("ocorrencias", []),
                "dica": "Use read_documento(document_id, offset=posicao) para ler ao redor de uma ocorrencia especifica.",
            }

            try:
                get_usage_logger().log_tool_call(
                    tool_name="localizar_no_documento",
                    duration_ms=ms_since(_start),
                    user_id=user_id,
                    user_email=user_email,
                    params={
                        "document_id": document_id,
                        "termo": termo,
                        "contexto_chars": contexto_chars,
                        "max_ocorrencias": max_ocorrencias,
                    },
                )
            except Exception:
                pass
            return resultado
        except Exception as e:
            try:
                get_usage_logger().log_tool_call(
                    tool_name="localizar_no_documento",
                    duration_ms=ms_since(_start),
                    user_id=user_id,
                    user_email=user_email,
                    success=False,
                    error_message=str(e),
                )
            except Exception:
                pass
            return erro_instrutivo(str(e), "Verifique o document_id e o termo.")
