import httpx
import logging
from typing import Optional

from ..core.config import settings

logger = logging.getLogger(__name__)

IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".tiff", ".tif"}


async def _graphiti_search(query: str, top_k: int = 5) -> list[dict]:
    graphiti_url = getattr(settings, "GRAPHITI_BASE_URL", "http://localhost:2082")
    async with httpx.AsyncClient(timeout=30.0) as client:
        try:
            res = await client.post(
                f"{graphiti_url}/api/knowledge-graph/query",
                json={"query": query},
            )
            if res.status_code != 200:
                logger.warning(f"Graphiti query error: {res.text[:200]}")
                return []
            data = res.json()
            results = data.get("results") or data.get("data") or []
            if isinstance(results, list):
                return [
                    {
                        "node_id": str(r.get("id", r.get("node_id", ""))),
                        "label": r.get("label", ""),
                        "title": r.get("name", r.get("title", "")),
                        "properties": r.get("properties", {}),
                        "source_type": "graph",
                        "source": "graphiti",
                        "score": 0.7,
                        "link": f"knowledge://graph/{r.get('id', '')}",
                    }
                    for r in results[:top_k]
                ]
        except Exception as e:
            logger.warning(f"Graphiti search failed: {e}")
    return []


def _merge_and_rank(
    graphiti_results: list[dict],
    top_k: int = 10,
) -> list[dict]:
    all_results = []

    for r in graphiti_results:
        all_results.append({
            "title": r.get("title", ""),
            "content": r.get("properties", {}).get("description", "")[:800],
            "source": "graphiti",
            "score": r.get("score", 0),
            "link": r.get("link", ""),
            "raw": r,
        })

    seen_titles = set()
    deduped = []
    for r in all_results:
        title_key = r["title"].strip().lower()
        if title_key and title_key not in seen_titles:
            seen_titles.add(title_key)
            deduped.append(r)

    max_score = max((r["score"] for r in deduped), default=1)
    if max_score > 0:
        for r in deduped:
            r["score"] = r["score"] / max_score

    deduped.sort(key=lambda x: x["score"], reverse=True)
    return deduped[:top_k]


async def parallel_search(
    query: str,
    document_ids: Optional[list[str]] = None,
    top_k: int = 5,
) -> dict:
    """
    搜索知识库（Graphiti）。

    Args:
        query: 搜索查询文本
        document_ids: 文档ID列表（用于RAG）
        top_k: 返回结果数量

    Returns:
        {
            "graphiti": [...],         # Graphiti搜索结果
            "merged_ranked": [...],    # 排序后的结果
        }
    """
    graphiti_task = _graphiti_search(query, top_k)

    graphiti_results = await graphiti_task

    if isinstance(graphiti_results, Exception):
        logger.error(f"Graphiti search exception: {graphiti_results}")
        graphiti_results = []

    merged_ranked = _merge_and_rank(graphiti_results, top_k * 2)

    return {
        "graphiti": graphiti_results,
        "merged_ranked": merged_ranked,
    }


def build_knowledge_context_prompt(
    kb_scope: dict,
    search_result: dict,
    user_memory: Optional[str] = None,
    recent_searches: Optional[list] = None,
) -> str:
    parts = []

    scope_type = kb_scope.get("context_type", "full_kb")
    collections = kb_scope.get("collections", [])
    doc_ids = kb_scope.get("document_ids", [])

    scope_desc = "全部知识库"
    if scope_type == "collection" and collections:
        scope_desc = f"限定集合: {', '.join(collections)}"
    elif scope_type == "documents" and doc_ids:
        scope_desc = f"限定文档 ({len(doc_ids)}份)"

    parts.append(f"=== 知识库范围 ===\n当前范围: {scope_desc}\n请在以上范围内检索和回答。")

    merged = search_result.get("merged_ranked", [])
    if merged:
        ref_lines = ["\n=== 检索结果（基于用户问题预检索）==="]
        for i, r in enumerate(merged[:10], 1):
            link = r.get('link', '')
            ref_lines.append(
                f"{i}. [{r['source'].upper()}] {r['title']}\n"
                f"   内容: {r['content'][:300]}\n"
                f"   链接: {link}"
            )
        parts.append("\n".join(ref_lines))

    if recent_searches:
        parts.append(f"\n=== 近期检索摘要 ===\n{chr(10).join(str(s) for s in recent_searches[:3])}")

    if user_memory:
        parts.append(f"\n=== 用户记忆 ===\n{user_memory[:300]}")

    parts.append("""

=== 回答规范 ===
你是一个法律知识库助手。请严格基于以上检索到的文档内容回答问题，
不要超出文档范围进行推测。

格式要求:
1. 在回答正文中引用具体法条/案例/裁量基准时，**必须使用上方检索结果中提供的真实链接**：
   - 格式: [《文档标题》](链接)
   - 链接必须直接复制上方检索结果中的"链接"字段，不要修改、不要使用占位符
   - 正确示例: "根据[《中华人民共和国食品安全法》](/knowledge/detail/12345?collection=market_regulation_laws)第三十四条..."
   - 错误示例: 使用 xxx 占位符或 https:// 前缀
   - 每个被引用的文档至少要在正文中出现一次带链接的引用

2. 回答末尾用空行后跟"📎 **引用来源**："分隔
   - 每条引用格式: "N. [《文档标题》](链接)"
   - ALL 引用的文档必须包含在引用来源中

3. 引用来源的顺序应与回答中出现的顺序一致
4. 如果没有引用任何文档，则不显示引用来源
5. 如果知识库不包含相关信息，明确告知用户"当前知识库不包含该信息"
""")

    return "\n".join(parts)