import json
import requests
from typing import List, Dict, Any, Optional

import jingrow
from jingrow.ai.utils.utils import get_jingrow_api_headers, get_jingrow_api_url
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
from qdrant_client.http import models
import re
import uuid


def call_jingrow_embedding_api(texts: List[str]) -> Dict[str, Any]:
    """
    调用Jingrow文本转向量API（仅使用 /jembedding/batch 流式端点）
    
    Args:
        texts: 要转换的文本列表
    
    Returns:
        {"success": bool, "data": List[Dict]}
    """
    api_url = f"{get_jingrow_api_url()}/jembedding/batch"
    headers = get_jingrow_api_headers()
    if not headers:
        jingrow.log_error("Embedding API 配置缺失", "未设置 Jingrow Api Key/Secret")
        return {"success": False, "message": "API密钥未设置"}
    
    request_data = {"items": texts}
    try:
        response = requests.post(api_url, json=request_data, headers=headers, stream=True, timeout=180)
        if response.status_code != 200:
            jingrow.log_error("Embedding API 调用失败", f"HTTP {response.status_code}")
            return {"success": False, "message": f"HTTP错误: {response.status_code}"}
        
        results: List[Dict[str, Any]] = []
        for line in response.iter_lines():
            if line:
                try:
                    result = json.loads(line)
                    results.append(result)
                except Exception:
                    continue
        return {"success": True, "data": results}
    except Exception as e:
        jingrow.log_error("调用 Embedding API 异常", str(e))
        return {"success": False, "message": str(e)}


def get_qdrant_client():
    """
    获取Qdrant客户端连接
    
    Returns:
        QdrantClient实例或None
    """
    try:
        client = QdrantClient(host="192.168.2.200", port=6333)
        # 测试连接
        client.get_collections()
        return client
    except Exception as e:
        jingrow.log_error("连接Qdrant失败", str(e))
        return None


def ensure_collection_exists(collection_name: str, vector_size: int = 1024) -> bool:
    """
    确保Qdrant集合存在，如果不存在则创建
    
    Args:
        collection_name: 集合名称
        vector_size: 向量维度大小
    
    Returns:
        是否成功
    """
    client = get_qdrant_client()
    if not client:
        return False
    
    try:
        # 检查集合是否存在
        collections = client.get_collections()
        collection_names = [col.name for col in collections.collections]
        
        if collection_name not in collection_names:
            # 创建集合
            client.create_collection(
                collection_name=collection_name,
                vectors_config=VectorParams(size=vector_size, distance=Distance.COSINE)
            )
            send_notification(f"创建Qdrant集合: {collection_name}")
        
        return True
    except Exception as e:
        jingrow.log_error("操作Qdrant集合失败", str(e))
        return False


def insert_vectors(collection_name: str, points: List[Dict[str, Any]]) -> bool:
    """
    向Qdrant插入向量数据
    
    Args:
        collection_name: 集合名称
        points: 点数据列表，每个点包含id, vector, payload
    
    Returns:
        是否成功
    """
    client = get_qdrant_client()
    if not client:
        return False
    
    try:
        # 确保集合存在
        if not ensure_collection_exists(collection_name):
            return False
        
        # 构建点结构（确保有必填 id）
        point_structs = []
        for point in points:
            # 统一：向量ID 使用 UUIDv5(基于 pagetype-name 确定性生成)
            payload_for_id = point.get("payload", {}) or {}
            record_pagetype = str(payload_for_id.get("pagetype", "")).strip()
            record_name = str(payload_for_id.get("name", "")).strip()
            if not record_pagetype or not record_name:
                raise ValueError("payload 缺少 pagetype 或 name，无法生成向量ID")
            deterministic_key = f"{record_pagetype}|{record_name}"
            point_id = str(uuid.uuid5(uuid.NAMESPACE_URL, f"jingrow://qdrant/{deterministic_key}"))
            point_structs.append(PointStruct(
                id=point_id,
                vector=point["vector"],
                payload=payload_for_id
            ))
        
        # 插入数据
        client.upsert(collection_name=collection_name, points=point_structs)
        return True
    except Exception as e:
        jingrow.log_error("插入向量数据失败", str(e))
        return False


def search_vectors(collection_name: str, query_vector: List[float], limit: int = 10, 
                  score_threshold: float = 0.7) -> List[Dict[str, Any]]:
    """
    在Qdrant中搜索相似向量
    
    Args:
        collection_name: 集合名称
        query_vector: 查询向量
        limit: 返回结果数量限制
        score_threshold: 相似度阈值
    
    Returns:
        搜索结果列表
    """
    client = get_qdrant_client()
    if not client:
        return []
    
    try:
        search_result = client.search(
            collection_name=collection_name,
            query_vector=query_vector,
            limit=limit,
            score_threshold=score_threshold
        )
        
        results = []
        for hit in search_result:
            results.append({
                "id": hit.id,
                "score": hit.score,
                "payload": hit.payload
            })
        
        return results
    except Exception as e:
        jingrow.log_error("搜索向量失败", str(e))
        return []


def delete_vectors(collection_name: str, point_ids: List[str]) -> bool:
    """
    从Qdrant删除向量数据
    
    Args:
        collection_name: 集合名称
        point_ids: 要删除的点ID列表
    
    Returns:
        是否成功
    """
    client = get_qdrant_client()
    if not client:
        return False
    
    try:
        client.delete(collection_name=collection_name, points_selector=models.PointIdsList(
            points=point_ids
        ))
        return True
    except Exception as e:
        jingrow.log_error("删除向量数据失败", str(e))
        return False


def update_vectors(collection_name: str, points: List[Dict[str, Any]]) -> bool:
    """
    更新Qdrant中的向量数据
    
    Args:
        collection_name: 集合名称
        points: 要更新的点数据列表
    
    Returns:
        是否成功
    """
    # 更新操作实际上就是插入操作（upsert）
    return insert_vectors(collection_name, points)


def create_text_embeddings(
    texts: List[str],
    collection_name: str = "knowledge_base",
    payload_meta: Optional[Dict[str, Any]] = None,
) -> bool:
    """
    创建文本向量并保存到Qdrant
    
    Args:
        texts: 文本列表
        collection_name: Qdrant集合名称
    
    Returns:
        是否成功
    """
    if not texts:
        return False
    
    # 调用API获取向量（统一使用batch端点）
    api_result = call_jingrow_embedding_api(texts)
    if not api_result.get("success", False):
        return False
    
    # 处理API返回的数据
    points = []
    for i, result in enumerate(api_result["data"]):
        if "embedding" in result:
            base_payload: Dict[str, Any] = {}
            if payload_meta:
                base_payload.update(payload_meta)
            points.append({
                "vector": result["embedding"],
                "payload": base_payload,
            })
    
    # 插入到Qdrant
    return insert_vectors(collection_name, points)


def delete_vectors_by_filter(
    collection_name: str,
    pagetype: str,
    name: str,
    field_name: Optional[str] = None,
) -> bool:
    """
    通过payload过滤条件删除向量数据（按pagetype + name，及可选field）
    """
    client = get_qdrant_client()
    if not client:
        return False
    try:
        must_conditions: List[models.FieldCondition] = [
            models.FieldCondition(key="pagetype", match=models.MatchValue(value=pagetype)),
            models.FieldCondition(key="name", match=models.MatchValue(value=name)),
        ]
        if field_name:
            must_conditions.append(models.FieldCondition(key="field", match=models.MatchValue(value=field_name)))
        flt = models.Filter(must=must_conditions)
        client.delete(collection_name=collection_name, points_selector=models.FilterSelector(filter=flt))
        return True
    except Exception as e:
        jingrow.log_error("按条件删除向量失败", str(e))
        return False


def create_text_embeddings_task(pagetype: str, pg_name: str) -> bool:
    """
    后台任务：为给定记录生成/更新文本向量（标题 + 内容摘要）。
    """
    try:
        pg = jingrow.get_pg(pagetype, pg_name)
        if not pg:
            return False

        # 清洗文本
        texts_to_process: List[str] = []
        title = getattr(pg, "title", None)
        content = getattr(pg, "content", None)
        if title and str(title).strip():
            texts_to_process.append(f"标题: {str(title).strip()}")
        content_text: Optional[str] = None
        if content and str(content).strip():
            raw_html = str(content).strip()
            raw_html = re.sub(r"(?i)<br\s*/?>|</p>|</li>|</div>|</h[1-6]>", " ", raw_html)
            clean_content = re.sub(r"<[^>]+>", "", raw_html)
            clean_content = clean_content.replace("\xa0", " ").replace("&nbsp;", " ")
            clean_content = re.sub(r"\s+", " ", clean_content).strip()
            if clean_content:
                from jingrow.ai.utils.utils import smart_truncate
                summary = smart_truncate(clean_content, 500)
                content_text = summary
                texts_to_process.append(f"内容: {summary}")

        if not texts_to_process:
            return True

        # 组装 payload 元信息
        payload_meta: Dict[str, Any] = {
            "pagetype": pagetype,
            "name": getattr(pg, "name", pg_name),
            "modified": getattr(pg, "modified", None),
            "title": getattr(pg, "title", None),
            "category": getattr(pg, "category", None),
            "content": content_text,
        }

        # 创建向量
        return create_text_embeddings(texts_to_process, "knowledge_base", payload_meta=payload_meta)
    except Exception as e:
        jingrow.log_error("创建文本向量任务失败", str(e))
        return False
