from fastapi import APIRouter, Depends, Query, Request
from ...core.database import get_db, get_system_db, _has_split_databases
from ...core.permission import get_current_user, is_admin_role
from ...core.config import settings
from ...core.model_resolver import get_active_embedding_config, get_active_rerank_config
import httpx
import json
import logging

logger = logging.getLogger(__name__)

router = APIRouter()


async def get_system_db_dep():
    """FastAPI 依赖：优先使用 system.db（kb_documents 所在库），单库模式回退到 get_db()"""
    _db_gen = get_system_db() if _has_split_databases() else get_db()
    async for db in _db_gen:
        yield db


@router.get("/config")
async def get_kb_config(request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user:
        return {"error": "未登录", "detail": "unauthorized"}

    conn, cur = db
    try:
        await cur.execute("SELECT config_key, config_value FROM system_configs WHERE config_key LIKE %s", ("kb_%",))
        rows = await cur.fetchall()
        config = {dict(r)["config_key"]: dict(r)["config_value"] for r in rows}
        return config
    except Exception:
        return {}


@router.put("/config")
async def update_kb_config(request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    conn, cur = db
    data = await request.json()
    valid_keys = {"kb_qdrant_host", "kb_qdrant_port", "kb_qdrant_collection", "kb_embedding_api_base",
                  "kb_embedding_api_key", "kb_embedding_model", "kb_rerank_model", "kb_rerank_enabled", "kb_markdown_dir"}

    for key, value in data.items():
        if key in valid_keys:
            await cur.execute(
                "INSERT INTO system_configs (config_key, config_value) VALUES (%s, %s) ON DUPLICATE KEY UPDATE config_value = %s",
                (key, str(value), str(value)),
            )

    return {"success": True}


@router.get("/list")
async def list_knowledge_bases(
    request: Request,
    page: int = Query(1, ge=1),
    pageSize: int = Query(10, ge=1, le=100),
    keyword: str = Query(""),
    db=Depends(get_system_db_dep),
):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    raw_uid = current_user.get("userId", "1")
    try:
        user_id = int(raw_uid)
    except (ValueError, TypeError):
        user_id = 1

    conn, cur = db
    try:
        where_clause = "WHERE deleted_at IS NULL"
        params = []
        if keyword:
            where_clause += " AND (display_name LIKE %s OR collection_name LIKE %s OR description LIKE %s)"
            like_val = f"%{keyword}%"
            params.extend([like_val, like_val, like_val])

        await cur.execute(f"SELECT COUNT(*) as total FROM qdrant_collections_config {where_clause}", tuple(params))
        total_row = await cur.fetchone()
        total = dict(total_row)["total"] if total_row else 0

        offset = (page - 1) * pageSize
        await cur.execute(
            f"""
            SELECT id, collection_name, display_name, description, vector_size,
                   distance_metric, category, document_count, status, created_at
            FROM qdrant_collections_config
            {where_clause}
            ORDER BY created_at DESC
            LIMIT %s OFFSET %s
            """,
            tuple(params + [pageSize, offset]),
        )
        rows = await cur.fetchall()

        collections = []
        for r in rows:
            row = dict(r)
            collections.append({
                "id": row["id"],
                "name": row["display_name"] or row["collection_name"],
                "collection_name": row["collection_name"],
                "description": row["description"] or "",
                "document_count": row["document_count"] or 0,
                "vector_size": row["vector_size"],
                "distance_metric": row["distance_metric"],
                "category": row["category"] or "",
                "status": row["status"],
                "created_at": str(row["created_at"]) if row["created_at"] else "",
            })

        return {
            "list": collections,
            "total": total,
        }
    except Exception as e:
        return {"error": "获取知识库列表失败", "detail": str(e)}


@router.post("/list")
async def create_knowledge_base(request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    conn, cur = db
    data = await request.json()
    name = data.get("name", "").strip()
    description = data.get("description", "").strip()

    if not name:
        return {"error": "集合名称不能为空", "detail": "name required"}

    collection_name = data.get("collection_name", "")
    if not collection_name:
        import re
        collection_name = re.sub(r"[^a-zA-Z0-9_]", "_", name).lower()
        collection_name = re.sub(r"_+", "_", collection_name).strip("_")
        if not collection_name:
            collection_name = "collection_" + str(int(__import__("time").time()))

    try:
        await cur.execute(
            "SELECT id FROM qdrant_collections_config WHERE collection_name = %s AND deleted_at IS NULL",
            (collection_name,),
        )
        if await cur.fetchone():
            return {"error": "集合标识已存在", "detail": "collection_name exists"}

        user_id = current_user.get("userId", 1)
        if isinstance(user_id, str) and user_id.isdigit():
            user_id = int(user_id)
        else:
            user_id = 1

        await cur.execute(
            """
            INSERT INTO qdrant_collections_config
            (collection_name, display_name, description, vector_size, distance_metric, category, status, created_by, created_at)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, NOW())
            """,
            (collection_name, name, description, 1024, "cosine", "", "active", user_id),
        )
        new_id = cur.lastrowid

        qdrant_host = settings.QDRANT_HOST or "localhost"
        qdrant_port = settings.QDRANT_PORT or "6334"
        async with httpx.AsyncClient(timeout=15) as client:
            create_res = await client.put(
                f"http://{qdrant_host}:{qdrant_port}/collections/{collection_name}",
                json={
                    "vectors": {"size": 1024, "distance": "Cosine"},
                    "hnsw_config": {"m": 16, "ef_construct": 100},
                },
            )
            if create_res.status_code not in (200, 201):
                await conn.rollback()
                return {"error": f"Qdrant集合创建失败: HTTP {create_res.status_code}", "detail": create_res.text}

        return {"success": True, "id": new_id, "message": "创建成功"}
    except Exception as e:
        return {"error": "创建失败", "detail": str(e)}


@router.put("/list/{id}")
async def update_knowledge_base(id: int, request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    conn, cur = db
    data = await request.json()
    name = data.get("name", "").strip()
    description = data.get("description", "").strip()

    if not name:
        return {"error": "集合名称不能为空", "detail": "name required"}

    try:
        await cur.execute(
            "UPDATE qdrant_collections_config SET display_name = %s, description = %s, updated_at = NOW() WHERE id = %s AND deleted_at IS NULL",
            (name, description, id),
        )
        if cur.rowcount == 0:
            return {"error": "集合不存在", "detail": "not found"}

        return {"success": True, "message": "更新成功"}
    except Exception as e:
        return {"error": "更新失败", "detail": str(e)}


@router.delete("/list/{id}")
async def delete_knowledge_base(id: int, request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    raw_uid = current_user.get("userId", "1")
    try:
        user_id = int(raw_uid)
    except (ValueError, TypeError):
        user_id = 1

    conn, cur = db
    try:
        await cur.execute(
            "SELECT collection_name FROM qdrant_collections_config WHERE id = %s AND deleted_at IS NULL",
            (id,),
        )
        row = await cur.fetchone()
        if not row:
            return {"error": "集合不存在", "detail": "not found"}
        collection_name = dict(row)["collection_name"]

        await cur.execute(
            "UPDATE qdrant_collections_config SET status = 'deleted', deleted_at = NOW() WHERE id = %s AND deleted_at IS NULL",
            (id,),
        )
        if cur.rowcount == 0:
            return {"error": "集合不存在", "detail": "not found"}

        return {"success": True, "message": "本地记录已删除（Qdrant数据未受影响，可通过同步恢复）"}
    except Exception as e:
        return {"error": "删除失败", "detail": str(e)}


@router.get("/documents")
async def list_documents(
    request: Request,
    page: int = Query(1, ge=1),
    pageSize: int = Query(10, ge=1, le=100),
    keyword: str = Query(""),
    db=Depends(get_system_db_dep),
):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    raw_uid = current_user.get("userId", "1")
    try:
        user_id = int(raw_uid)
    except (ValueError, TypeError):
        user_id = 1

    conn, cur = db
    try:
        where_clause = "WHERE deleted_at IS NULL"
        params = []
        if keyword:
            where_clause += " AND (title LIKE %s OR file_name LIKE %s)"
            like_val = f"%{keyword}%"
            params.extend([like_val, like_val])

        await cur.execute(f"SELECT COUNT(*) as total FROM raw_documents {where_clause}", tuple(params))
        total_row = await cur.fetchone()
        total = dict(total_row)["total"] if total_row else 0

        offset = (page - 1) * pageSize
        await cur.execute(
            f"""
            SELECT id, title, file_name, file_type, status, upload_time
            FROM raw_documents
            {where_clause}
            ORDER BY upload_time DESC
            LIMIT %s OFFSET %s
            """,
            tuple(params + [pageSize, offset]),
        )
        rows = await cur.fetchall()

        documents = []
        for r in rows:
            row = dict(r)
            status = row.get("status", "pending")
            documents.append({
                "id": row["id"],
                "name": row["title"] or row["file_name"] or "",
                "file_name": row["file_name"] or "",
                "file_type": row["file_type"] or "",
                "status": "indexed" if status in ("completed",) else ("pending" if status in ("pending", "processing") else status),
                "upload_time": str(row["upload_time"]) if row["upload_time"] else "",
            })

        return {
            "list": documents,
            "total": total,
        }
    except Exception as e:
        return {"error": "获取文档列表失败", "detail": str(e)}


@router.get("/status")
async def get_kb_status(
    request: Request,
    host: str = Query("localhost"),
    port: str = Query("6334"),
    collection: str = Query("market_regulation_laws"),
    db=Depends(get_system_db_dep),
):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    try:
        async with httpx.AsyncClient(timeout=5) as client:
            res = await client.get(f"http://{host}:{port}/collections/{collection}")
            if res.status_code != 200:
                return {"connected": False, "error": f"Qdrant返回状态码 {res.status_code}"}
            data = res.json()
            result = data.get("result", {})
            return {
                "connected": True,
                "collection": collection,
                "points_count": result.get("points_count", 0),
                "vector_size": result.get("config", {}).get("params", {}).get("vectors", {}).get("size", 0),
                "distance": result.get("config", {}).get("params", {}).get("vectors", {}).get("distance", "N/A"),
                "status": result.get("status", "N/A"),
            }
    except Exception as e:
        return {"connected": False, "error": str(e)}


@router.post("/search")
async def search_kb(request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    body = await request.json()
    query = body.get("query")
    if not query:
        return {"error": "缺少查询文本", "detail": "query required"}

    try:
        embed_cfg = await get_active_embedding_config()
        api_key = body.get("apiKey") or embed_cfg.get("api_key", "")
        api_base = body.get("apiBase") or embed_cfg.get("api_base", "https://api.siliconflow.cn/v1")
        model = body.get("model") or embed_cfg.get("model_name", "BAAI/bge-m3")

        qdrant_host = body.get("qdrantHost") or settings.QDRANT_HOST or "localhost"
        qdrant_port = body.get("qdrantPort") or settings.QDRANT_PORT or "6334"
        collection = body.get("collection") or settings.QDRANT_COLLECTION_NAME or "market_regulation_laws"
        limit = min(body.get("limit", 10), 20)
        rerank_enabled = body.get("rerank", False)

        async with httpx.AsyncClient(timeout=30) as client:
            embed_res = await client.post(
                f"{api_base}/embeddings",
                headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
                json={"model": model, "input": [query], "encoding_format": "float"},
            )
            if embed_res.status_code != 200:
                return {"error": "调用Embedding API失败", "detail": embed_res.text}
            vector = embed_res.json()["data"][0]["embedding"]

            search_body: dict = {
                "vector": vector,
                "limit": limit,
                "with_payload": True,
            }
            filter_payload_type = body.get("filterType")
            if filter_payload_type:
                search_body["filter"] = {"must": [{"key": "type", "match": {"value": filter_payload_type}}]}

            qdrant_res = await client.post(
                f"http://{qdrant_host}:{qdrant_port}/collections/{collection}/points/search",
                json=search_body,
            )
            if qdrant_res.status_code != 200:
                return {"error": "Qdrant搜索错误", "detail": qdrant_res.text}

            results = []
            for r in qdrant_res.json().get("result", []):
                payload = r.get("payload", {})
                content = payload.get("content", "")
                results.append({
                    "id": str(r.get("id", ""))[:24],
                    "score": round(r.get("score", 0), 4),
                    "chunk_id": payload.get("chunk_id", ""),
                    "type": payload.get("type", ""),
                    "title": payload.get("title", payload.get("source_file", "")),
                    "region": payload.get("region", ""),
                    "article_number": payload.get("article_number", ""),
                    "content_preview": content[:300] + ("..." if len(content) > 300 else ""),
                    "content_length": len(content),
                })

            rerank_model_used = None
            if rerank_enabled and len(results) > 1:
                try:
                    rerank_cfg = await get_active_rerank_config()
                    rerank_model = rerank_cfg.get("model_name", "BAAI/bge-reranker-v2-m3")
                    rerank_base = rerank_cfg.get("api_base", api_base)
                    rerank_key = rerank_cfg.get("api_key", api_key)
                    documents = [f"{r['title']} {r.get('article_number', '')} {r['content_preview']}" for r in results]
                    async with httpx.AsyncClient(timeout=30) as rerank_client:
                        rerank_res = await rerank_client.post(
                            f"{rerank_base}/rerank",
                            headers={"Authorization": f"Bearer {rerank_key}", "Content-Type": "application/json"},
                            json={"model": rerank_model, "query": query, "documents": documents, "top_n": limit, "return_documents": False},
                        )
                        if rerank_res.status_code == 200:
                            rerank_data = rerank_res.json()
                            reranked = []
                            for rr in rerank_data.get("results", []):
                                idx = rr.get("index", -1)
                                if 0 <= idx < len(results):
                                    results[idx]["score"] = round(rr.get("relevance_score", results[idx]["score"]), 4)
                                    reranked.append(results[idx])
                            results = sorted(reranked, key=lambda x: x["score"], reverse=True)[:limit]
                            rerank_model_used = rerank_model
                except Exception as rerank_err:
                    logger.warning(f"Rerank failed (admin search): {rerank_err}")

            return {
                "results": results,
                "model": model,
                "collection": collection,
                "total": len(results),
                "reranked": rerank_enabled,
                "rerank_model": rerank_model_used,
            }
    except Exception as e:
        return {"error": "搜索失败", "detail": str(e)}


@router.post("/import")
async def import_kb(request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    body = await request.json()
    host = body.get("qdrantHost") or settings.QDRANT_HOST or "localhost"
    port = body.get("qdrantPort") or settings.QDRANT_PORT or "6334"
    collection = body.get("collection") or settings.QDRANT_COLLECTION_NAME or "market_regulation_laws"

    try:
        async with httpx.AsyncClient(timeout=5) as client:
            res = await client.get(f"http://{host}:{port}/collections/{collection}")
            if res.status_code != 200:
                return {"error": "无法连接Qdrant", "detail": "connection failed"}
            data = res.json()
            points_count = data.get("result", {}).get("points_count", 0)
            return {
                "success": True,
                "message": f"知识库导入任务已提交。当前集合 \"{collection}\" 有 {points_count} 条数据。",
                "collection": collection,
                "current_count": points_count,
            }
    except Exception as e:
        return {"error": "操作失败", "detail": str(e)}


@router.get("/collections/sync")
async def sync_qdrant_collections(request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    raw_uid = current_user.get("userId", "1")
    try:
        user_id = int(raw_uid)
    except (ValueError, TypeError):
        user_id = 1

    conn, cur = db
    try:
        qdrant_host = settings.QDRANT_HOST or "localhost"
        qdrant_port = settings.QDRANT_PORT or "6334"

        async with httpx.AsyncClient(timeout=15) as client:
            res = await client.get(f"http://{qdrant_host}:{qdrant_port}/collections")
            if res.status_code != 200:
                return {"error": f"无法连接Qdrant服务: HTTP {res.status_code}", "detail": "connection failed"}

            data = res.json()
            qdrant_collections = data.get("result", {}).get("collections", [])

            synced_count = 0
            for qc in qdrant_collections:
                name = qc.get("name")

                detail_res = await client.get(f"http://{qdrant_host}:{qdrant_port}/collections/{name}")
                if detail_res.status_code != 200:
                    continue

                detail = detail_res.json().get("result", {})
                config = detail.get("config", {})
                params = config.get("params", {})
                vectors = params.get("vectors", {})
                vector_size = vectors.get("size", 0) if isinstance(vectors, dict) else 0
                distance = vectors.get("distance", "Unknown") if isinstance(vectors, dict) else "Unknown"
                points_count = detail.get("points_count", 0)
                coll_status = detail.get("status", "unknown")
                status_map = {
                    "green": "active",
                    "yellow": "building",
                    "red": "error",
                    "grey": "disabled",
                }
                coll_status = status_map.get(coll_status, "active")

                await cur.execute(
                    """INSERT INTO qdrant_collections_config
                       (collection_name, display_name, description, vector_size, distance_metric,
                        document_count, status, created_by, created_at)
                       VALUES (%s, %s, %s, %s, %s, %s, %s, %s, NOW())
                       ON DUPLICATE KEY UPDATE
                       display_name = VALUES(display_name),
                       vector_size = VALUES(vector_size),
                       distance_metric = VALUES(distance_metric),
                       document_count = VALUES(document_count),
                       status = VALUES(status),
                       updated_at = NOW()""",
                    (name, name, f"从Qdrant同步: {name}", vector_size, distance, points_count, coll_status, user_id),
                )
                synced_count += 1

            return {
                "success": True,
                "synced": synced_count,
                "message": f"成功同步 {synced_count} 个集合",
                "collections": [{"name": c["name"], "points": 0} for c in qdrant_collections],
            }
    except Exception as e:
        return {"error": f"同步失败: {str(e)}", "detail": str(e)}


@router.get("/collections/{collection_name}/detail")
async def get_collection_detail(collection_name: str, request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    try:
        qdrant_host = settings.QDRANT_HOST or "localhost"
        qdrant_port = settings.QDRANT_PORT or "6334"

        async with httpx.AsyncClient(timeout=10) as client:
            res = await client.get(f"http://{qdrant_host}:{qdrant_port}/collections/{collection_name}")
            if res.status_code != 200:
                return {"error": f"获取集合详情失败: HTTP {res.status_code}", "detail": "not found"}

            data = res.json().get("result", {})
            config = data.get("config", {})
            params = config.get("params", {})
            vectors = params.get("vectors", {})

            return {
                "success": True,
                "name": collection_name,
                "points_count": data.get("points_count", 0),
                "segments_count": data.get("segments_count", 0),
                "vector_size": vectors.get("size", 0) if isinstance(vectors, dict) else 0,
                "distance": vectors.get("distance", "N/A") if isinstance(vectors, dict) else "N/A",
                "status": data.get("status", "N/A"),
            }
    except Exception as e:
        return {"error": f"获取集合详情失败: {str(e)}", "detail": str(e)}


@router.get("/stats")
async def get_kb_stats(request: Request, db=Depends(get_system_db_dep)):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        return {"error": "无权限", "detail": "admin required"}

    conn, cur = db
    stats = {}

    try:
        await cur.execute("SELECT COUNT(*) as total FROM kb_documents WHERE deleted_at IS NULL")
        total_docs = (await cur.fetchone())["total"]
        stats["total_documents"] = total_docs

        await cur.execute("SELECT review_status, COUNT(*) as cnt FROM kb_documents WHERE deleted_at IS NULL GROUP BY review_status")
        status_rows = await cur.fetchall()
        stats["documents_by_status"] = {dict(r)["review_status"]: dict(r)["cnt"] for r in status_rows}

        await cur.execute("SELECT COUNT(*) as total FROM kb_md_documents WHERE deleted_at IS NULL")
        total_md = (await cur.fetchone())["total"]
        stats["total_md_documents"] = total_md

        await cur.execute("SELECT import_status, COUNT(*) as cnt FROM kb_md_documents WHERE deleted_at IS NULL GROUP BY import_status")
        md_status_rows = await cur.fetchall()
        stats["md_by_import_status"] = {dict(r)["import_status"]: dict(r)["cnt"] for r in md_status_rows}

        await cur.execute("SELECT COUNT(*) as total FROM qdrant_collections_config WHERE deleted_at IS NULL")
        total_collections = (await cur.fetchone())["total"]
        stats["total_collections"] = total_collections

        async with httpx.AsyncClient(timeout=5) as client:
            res = await client.get(f"http://{settings.QDRANT_HOST}:{settings.QDRANT_PORT}/collections")
            if res.status_code == 200:
                collections_data = res.json().get("result", {}).get("collections", [])
                stats["qdrant_collections"] = len(collections_data)
                stats["qdrant_total_points"] = sum(c.get("points_count", 0) for c in collections_data)

    except Exception as e:
        stats["error"] = str(e)

    return stats
