"""Lightweight vector search tool using Qdrant + SiliconFlow Embedding API.

Zero heavy local dependencies:
- No PyTorch (~1.1GB), no sentence-transformers, no fastembed needed
- Uses SiliconFlow cloud API for embedding (BAAI/bge-large-zh-v1.5, 1024-dim)
- Uses SiliconFlow cloud API for reranking (BAAI/bge-reranker-v2-m3)
- Only requires: qdrant-client + httpx (already installed)

Supported collections:
- market_regulation_laws: 法律法规 (search_laws / get_law_article)
- case_precedents: 处罚案例 (search_cases)
- discretion_standards: 处罚裁量基准 (search_discretion)

Config source:
- All config comes from Settings class (app.core.config) which reads .env
- No BFF API model resolver — environment variables only
"""

import logging
from typing import Optional

import httpx
from langchain.tools import tool

from app.core.config import settings

logger = logging.getLogger(__name__)

_client = None
_embedding_config_cache: dict | None = None
_rerank_config_cache: dict | None = None


def _resolve_embedding_config() -> dict:
    """Resolve embedding config from Settings / env vars."""
    global _embedding_config_cache
    if _embedding_config_cache is not None:
        return _embedding_config_cache

    api_key = settings.EMBEDDING_API_KEY or settings.SILICONFLOW_API_KEY
    model_name = settings.EMBEDDING_MODEL
    if model_name == "BAAI/bge-large-zh":
        model_name = "BAAI/bge-large-zh-v1.5"

    cfg = {
        "api_base": settings.EMBEDDING_API_BASE.rstrip("/"),
        "api_key": api_key,
        "model": model_name,
    }
    _embedding_config_cache = cfg
    logger.info(f"Embedding config resolved: model={cfg['model']}")
    return cfg


def _resolve_rerank_config() -> dict:
    """Resolve rerank config from Settings / env vars."""
    global _rerank_config_cache
    if _rerank_config_cache is not None:
        return _rerank_config_cache

    api_key = settings.EMBEDDING_API_KEY or settings.SILICONFLOW_API_KEY
    cfg = {
        "api_base": settings.SILICONFLOW_API_BASE.rstrip("/"),
        "api_key": api_key,
        "model": settings.RERANK_MODEL,
        "enabled": settings.RERANK_ENABLED.lower() == "true",
    }
    _rerank_config_cache = cfg
    logger.info(f"Rerank config resolved: model={cfg['model']}, enabled={cfg['enabled']}")
    return cfg


def _invalidate_config_cache() -> None:
    """Invalidate cached config so next call re-reads from Settings."""
    global _embedding_config_cache, _rerank_config_cache
    _embedding_config_cache = None
    _rerank_config_cache = None


def _get_client():
    """Create or return cached QdrantClient singleton."""
    global _client
    if _client is None:
        from qdrant_client import QdrantClient

        _client = QdrantClient(host=settings.QDRANT_HOST, port=settings.QDRANT_PORT)
    return _client


def _embed_query(query: str) -> list[float]:
    """Call SiliconFlow embedding API to get query vector."""
    cfg = _resolve_embedding_config()
    response = httpx.post(
        f"{cfg['api_base']}/embeddings",
        headers={
            "Authorization": f"Bearer {cfg['api_key']}",
            "Content-Type": "application/json",
        },
        json={
            "model": cfg["model"],
            "input": [query[:512]],
            "encoding_format": "float",
        },
        timeout=30.0,
    )
    response.raise_for_status()
    data = response.json()
    return data["data"][0]["embedding"]


def _rerank(query: str, documents: list[str], top_n: int = 5) -> list[dict]:
    """Call SiliconFlow rerank API to re-order documents by relevance."""
    cfg = _resolve_rerank_config()
    if not cfg["enabled"] or not cfg["api_key"]:
        return [{"index": i, "relevance_score": 1.0} for i in range(len(documents))]

    try:
        response = httpx.post(
            f"{cfg['api_base']}/rerank",
            headers={
                "Authorization": f"Bearer {cfg['api_key']}",
                "Content-Type": "application/json",
            },
            json={
                "model": cfg["model"],
                "query": query,
                "documents": documents,
                "top_n": top_n,
                "return_documents": False,
            },
            timeout=30.0,
        )
        response.raise_for_status()
        return response.json().get("results", [])
    except Exception as e:
        logger.warning(f"Rerank failed: {e}")
        return [{"index": i, "relevance_score": 1.0} for i in range(len(documents))]


def _search_collection(
    collection_name: str,
    query: str,
    limit: int = 5,
    region: Optional[str] = None,
    extra_filters: Optional[dict] = None,
) -> list[tuple]:
    """Generic collection search with optional reranking.

    Returns:
        List of (ScoredPoint, score) tuples sorted by relevance.
    """
    client = _get_client()
    limit = min(max(1, limit), 10)
    query_vector = _embed_query(query)

    from qdrant_client.models import Filter, FieldCondition, MatchValue

    must_conditions = []
    if region:
        must_conditions.append(FieldCondition(key="region", match=MatchValue(value=region)))
    if extra_filters:
        for key, value in extra_filters.items():
            must_conditions.append(FieldCondition(key=key, match=MatchValue(value=value)))

    query_filter = Filter(must=must_conditions) if must_conditions else None

    rerank_cfg = _resolve_rerank_config()
    rerank_enabled = rerank_cfg["enabled"] and bool(rerank_cfg["api_key"])
    search_limit = limit * 3 if rerank_enabled else limit

    results = client.query_points(
        collection_name=collection_name,
        query=query_vector,
        query_filter=query_filter,
        limit=search_limit,
    ).points

    if not results:
        return []

    if rerank_enabled and len(results) > 1:
        documents = []
        for r in results:
            p = r.payload or {}
            doc_parts = [p.get("title", ""), p.get("content", "")]
            if p.get("article_num"):
                doc_parts.insert(1, p["article_num"])
            if p.get("violation_type"):
                doc_parts.append(p["violation_type"])
            if p.get("penalty_summary"):
                doc_parts.append(p["penalty_summary"])
            documents.append(" ".join(doc_parts))

        rerank_results = _rerank(query, documents, top_n=limit)
        reranked = []
        for rr in rerank_results:
            idx = rr["index"]
            if idx < len(results):
                reranked.append((results[idx], rr.get("relevance_score", results[idx].score)))
        reranked.sort(key=lambda x: x[1], reverse=True)
        return reranked[:limit]
    else:
        return [(r, r.score) for r in results[:limit]]


# ---------------------------------------------------------------------------
# LangChain Tools
# ---------------------------------------------------------------------------


@tool("search_laws", parse_docstring=True)
def search_laws(query: str, limit: int = 5, region: Optional[str] = None) -> str:
    """Search the legal knowledge base for relevant laws and regulations.

    This tool searches through a vector database of Chinese market regulation laws
    and regulations to find the most relevant legal provisions for a given query.

    Use this tool when:
    - User asks about specific laws or regulations
    - User needs legal basis for administrative penalties
    - User wants to know how to handle specific violations
    - User needs to draft legal documents and needs relevant provisions

    Args:
        query: The search query describing the legal question or violation.
               e.g. "虚假广告处罚", "食品安全违法行为", "行政处罚程序"
        limit: Maximum number of results to return (default: 5, max: 10)
        region: Optional region filter (e.g., "北京", "上海", "全国")

    Returns:
        A formatted string containing the search results with:
        - Law/regulation title
        - Article number
        - Content excerpt
        - Similarity score
        - Region information
    """
    try:
        collection = settings.QDRANT_COLLECTION_NAME
        final_results = _search_collection(collection, query, limit, region)

        if not final_results:
            return f"未找到与 '{query}' 相关的法律法规。请尝试使用其他关键词搜索。"

        output_lines = [f"## 法律法规检索结果（查询：{query}）\n"]
        output_lines.append(f"找到 {len(final_results)} 条相关法规：\n")
        output_lines.append("> ⚠️ **版本时效提示**：以下法条引用请以国家法律法规数据库官方最新文本为准，注意确认法条修正/修订版本。")

        for i, (result, score) in enumerate(final_results, 1):
            payload = result.payload or {}
            title = payload.get("title", "未知法规")
            article = payload.get("article_num", "未知条款")
            content = payload.get("content", "")
            reg = payload.get("region", "")
            effective_date = payload.get("effective_date", "")
            publish_date = payload.get("publish_date", "")

            output_lines.append(f"### 结果 {i}（相似度：{score:.2%}）")
            output_lines.append(f"**法规名称**：{title}")
            output_lines.append(f"**条款**：{article}")
            if reg:
                output_lines.append(f"**地区**：{reg}")
            if effective_date:
                output_lines.append(f"**生效日期**：{effective_date}")
            if publish_date:
                output_lines.append(f"**公布日期**：{publish_date}")
            knowledge_url = f"/knowledge/detail/{result.id}?collection={collection}"
            output_lines.append(f"**知识库**：[查看完整法条]({knowledge_url})")
            excerpt = content[:300] + "..." if len(content) > 300 else content
            output_lines.append(f"**内容**：\n{excerpt}\n")

        return "\n".join(output_lines)

    except Exception as e:
        logger.error(f"Search laws error: {e}")
        return f"检索法律法规时发生错误：{str(e)}。请检查知识库配置是否正确。"


@tool("get_law_article", parse_docstring=True)
def get_law_article(law_title: str, article_number: str) -> str:
    """Get the full content of a specific article from a law or regulation.

    Use this tool when you need to retrieve the complete text of a specific
    article from a law or regulation that was found in a previous search.

    Args:
        law_title: The exact title of the law or regulation.
                   e.g. "中华人民共和国行政处罚法"
        article_number: The article number in Chinese.
                        e.g. "第五条", "第三十二条"

    Returns:
        The full content of the specified article, or an error message if not found.
    """
    try:
        client = _get_client()
        collection = settings.QDRANT_COLLECTION_NAME

        from qdrant_client.models import Filter, FieldCondition, MatchValue

        results, _ = client.scroll(
            collection_name=collection,
            scroll_filter=Filter(
                must=[
                    FieldCondition(key="title", match=MatchValue(value=law_title)),
                    FieldCondition(key="article_num", match=MatchValue(value=article_number)),
                ]
            ),
            limit=1,
        )

        if not results:
            return f"未找到法规 '{law_title}' 的 {article_number}。"

        point = results[0]
        payload = point.payload or {}
        content = payload.get("content", "")
        title = payload.get("title", "")
        article = payload.get("article_num", "")
        region = payload.get("region", "")
        effective_date = payload.get("effective_date", "")
        publish_date = payload.get("publish_date", "")

        lines = [f"## {title}", f"### {article}"]
        if region:
            lines.append(f"**地区**：{region}")
        if effective_date:
            lines.append(f"**生效日期**：{effective_date}")
        if publish_date:
            lines.append(f"**公布日期**：{publish_date}")
        lines.append("")
        lines.append("> ⚠️ **版本时效提示**：本条引用请以国家法律法规数据库官方最新文本为准，注意确认该法条是否为最新修正/修订版本。")
        lines.append("")
        lines.append("**全文**：\n" + content)
        return "\n".join(lines)

    except Exception as e:
        logger.error(f"Get law article error: {e}")
        return f"获取法规条款时发生错误：{str(e)}"


@tool("search_cases", parse_docstring=True)
def search_cases(query: str, limit: int = 5, region: Optional[str] = None) -> str:
    """Search the case precedent database for similar administrative penalty cases.

    This tool searches through a vector database of administrative penalty cases
    to find similar cases for reference in penalty decisions.

    Use this tool when:
    - User needs to find similar cases for penalty reference
    - User wants to compare how similar violations were handled
    - User asks about typical penalties for a specific violation
    - User needs case precedents to support a penalty decision

    Args:
        query: Description of the violation or case scenario.
               e.g. "销售过期食品罚款", "无证经营处罚案例", "虚假广告行政处罚"
        limit: Maximum number of results to return (default: 5, max: 10)
        region: Optional region filter (e.g., "新疆", "全国")

    Returns:
        A formatted string containing similar cases with:
        - Case title
        - Violation description
        - Penalty result
        - Legal basis
        - Similarity score
    """
    try:
        final_results = _search_collection("case_precedents", query, limit, region)

        if not final_results:
            return f"未找到与 '{query}' 相关的处罚案例。请尝试使用其他关键词搜索。"

        output_lines = [f"## 处罚案例检索结果（查询：{query}）\n"]
        output_lines.append(f"找到 {len(final_results)} 个类似案例：\n")

        for i, (result, score) in enumerate(final_results, 1):
            payload = result.payload or {}
            title = payload.get("title", "未知案例")
            content = payload.get("content", "")
            violation_type = payload.get("violation_type", "")
            penalty_summary = payload.get("penalty_summary", "")
            legal_basis = payload.get("legal_basis", "")
            reg = payload.get("region", "")
            case_type = payload.get("case_type", "")

            output_lines.append(f"### 案例 {i}（相似度：{score:.2%}）")
            output_lines.append(f"**案例名称**：{title}")
            if case_type:
                output_lines.append(f"**案例类型**：{case_type}")
            if violation_type:
                output_lines.append(f"**违法行为**：{violation_type}")
            if legal_basis:
                output_lines.append(f"**适用法条**：{legal_basis}")
            if penalty_summary:
                output_lines.append(f"**处罚结果**：{penalty_summary}")
            if reg:
                output_lines.append(f"**地区**：{reg}")
            knowledge_url = f"/knowledge/detail/{result.id}?collection=case_precedents"
            output_lines.append(f"**知识库**：[查看完整案例]({knowledge_url})")
            excerpt = content[:300] + "..." if len(content) > 300 else content
            output_lines.append(f"**案情摘要**：\n{excerpt}\n")

        return "\n".join(output_lines)

    except Exception as e:
        logger.error(f"Search cases error: {e}")
        return f"检索处罚案例时发生错误：{str(e)}。请检查知识库配置是否正确。"


@tool("search_discretion", parse_docstring=True)
def search_discretion(query: str, limit: int = 5, region: Optional[str] = None) -> str:
    """Search the discretion standards database for penalty guidelines.

    This tool searches through a vector database of administrative penalty
    discretion standards to find applicable penalty ranges and criteria.

    Use this tool when:
    - User needs to determine the appropriate penalty range for a violation
    - User wants to check discretion standards for a specific violation type
    - User asks about factors that affect penalty severity (从轻/一般/从重)
    - User needs to justify a penalty decision with discretion standards

    Args:
        query: Description of the violation for discretion lookup.
               e.g. "食品安全违法裁量基准", "虚假广告处罚幅度", "无证经营罚款标准"
        limit: Maximum number of results to return (default: 5, max: 10)
        region: Optional region filter (e.g., "新疆", "全国")

    Returns:
        A formatted string containing discretion standards with:
        - Standard title
        - Violation type
        - Penalty ranges (轻微/一般/严重)
        - Legal basis
        - Similarity score
    """
    try:
        final_results = _search_collection("discretion_standards", query, limit, region)

        if not final_results:
            return f"未找到与 '{query}' 相关的裁量基准。请尝试使用其他关键词搜索。"

        output_lines = [f"## 裁量基准检索结果（查询：{query}）\n"]
        output_lines.append(f"找到 {len(final_results)} 条裁量基准：\n")

        for i, (result, score) in enumerate(final_results, 1):
            payload = result.payload or {}
            title = payload.get("title", "未知裁量基准")
            content = payload.get("content", "")
            violation = payload.get("violation", "")
            legal_basis = payload.get("legal_basis", "")
            reg = payload.get("region", "")
            article_num = payload.get("article_num", "")

            output_lines.append(f"### 结果 {i}（相似度：{score:.2%}）")
            output_lines.append(f"**基准名称**：{title}")
            if article_num:
                output_lines.append(f"**条款**：{article_num}")
            if violation:
                output_lines.append(f"**违法事项**：{violation}")
            if legal_basis:
                output_lines.append(f"**法律依据**：{legal_basis}")
            if reg:
                output_lines.append(f"**地区**：{reg}")
            knowledge_url = f"/knowledge/detail/{result.id}?collection=discretion_standards"
            output_lines.append(f"**知识库**：[查看完整裁量文件]({knowledge_url})")
            excerpt = content[:400] + "..." if len(content) > 400 else content
            output_lines.append(f"**裁量标准**：\n{excerpt}\n")

        return "\n".join(output_lines)

    except Exception as e:
        logger.error(f"Search discretion error: {e}")
        return f"检索裁量基准时发生错误：{str(e)}。请检查知识库配置是否正确。"
