from fastapi import APIRouter, Depends, Query, Request, UploadFile, File
from typing import Optional
from ...core.database import get_db
from ...core.permission import get_current_user, is_admin_role
from ...core.config import settings
from ...services.chunk_engine import ChunkEngine, STRATEGY_LIST
from ...services.discretion_importer import DocumentImporter, ImportOptions
import httpx
import json
import os
from datetime import datetime

router = APIRouter()

GRAPHITI_SERVER_URL = getattr(settings, "GRAPHITI_BASE_URL", "http://localhost:2082")


def _check_admin(request: Request):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user.get("role", "")):
        return None, {"error": "无权限", "detail": "admin required"}
    return current_user, None


@router.get("/kb/md-documents")
async def list_md_documents(
    request: Request,
    page: int = Query(1, ge=1),
    pageSize: int = Query(20, ge=1, le=100),
    status: str = Query(""),
    category: str = Query(""),
    keyword: str = Query(""),
    contributor: str = Query(""),
    db=Depends(get_db),
):
    _, err = _check_admin(request)
    if err:
        return err

    conn, cur = db
    where = "WHERE deleted_at IS NULL"
    params = []

    if status:
        where += " AND import_status = %s"
        params.append(status)
    if category:
        where += " AND category = %s"
        params.append(category)
    if keyword:
        where += " AND (doc_name LIKE %s OR description LIKE %s)"
        like_val = f"%{keyword}%"
        params.extend([like_val, like_val])
    if contributor:
        where += " AND contributor_name LIKE %s"
        params.append(f"%{contributor}%")

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

    offset = (page - 1) * pageSize
    await cur.execute(
        f"""SELECT id, source_doc_id, doc_name, category, description,
                   import_status, wiki_page_id, qdrant_collection,
                   graphiti_imported, chunk_strategy, chunk_count,
                   file_type, csv_content,
                   region, issuer, effective_date, doc_version,
                   contributor_id, contributor_name,
                   source_upload_time, remark, created_at, updated_at
            FROM kb_md_documents {where}
            ORDER BY created_at DESC
            LIMIT %s OFFSET %s""",
        tuple(params + [pageSize, offset]) if params else (pageSize, offset),
    )
    rows = await cur.fetchall()
    items = []
    for r in rows:
        row = dict(r)
        items.append({
            "id": row["id"],
            "sourceDocId": row["source_doc_id"],
            "docName": row["doc_name"],
            "category": row["category"],
            "description": row["description"] or "",
            "importStatus": row["import_status"],
            "wikiPageId": row["wiki_page_id"] or "",
            "qdrantCollection": row["qdrant_collection"] or "",
            "graphitiImported": bool(row.get("graphiti_imported", 0)),
            "chunkStrategy": row.get("chunk_strategy") or "",
            "chunkCount": row.get("chunk_count") or 0,
            "fileType": row.get("file_type") or "md",
            "hasCsv": bool(row.get("csv_content")),
            "region": row.get("region") or "",
            "issuer": row.get("issuer") or "",
            "effectiveDate": str(row["effective_date"]) if row.get("effective_date") else "",
            "docVersion": row.get("doc_version") or "",
            "contributorId": row["contributor_id"],
            "contributorName": row["contributor_name"],
            "sourceUploadTime": str(row["source_upload_time"]) if row["source_upload_time"] else "",
            "remark": row["remark"] or "",
            "createdAt": str(row["created_at"]) if row["created_at"] else "",
            "updatedAt": str(row["updated_at"]) if row["updated_at"] else "",
        })

    return {"list": items, "total": total, "page": page, "pageSize": pageSize}


@router.post("/kb/md-documents/upload")
async def upload_md_document(
    request: Request,
    file: UploadFile = File(...),
    csvFile: Optional[UploadFile] = File(None),
    docName: str = Query(""),
    category: str = Query("other"),
    description: str = Query(""),
    region: str = Query(""),
    issuer: str = Query(""),
    effectiveDate: str = Query(""),
    docVersion: str = Query(""),
    db=Depends(get_db),
):
    current_user, err = _check_admin(request)
    if err:
        return err

    if not file.filename:
        return {"error": "请选择文件", "detail": "no file"}

    ext = file.filename.rsplit('.', 1)[-1].lower() if '.' in file.filename else ''
    if ext not in ('md', 'markdown', 'txt'):
        return {"error": f"主文件仅支持MD格式，当前格式: {ext}", "detail": "invalid file type"}

    content_bytes = await file.read()
    try:
        content = content_bytes.decode('utf-8')
    except UnicodeDecodeError:
        try:
            content = content_bytes.decode('gbk')
        except UnicodeDecodeError:
            content = content_bytes.decode('utf-8', errors='replace')

    csv_content = None
    file_type = 'md'
    if csvFile and csvFile.filename:
        csv_ext = csvFile.filename.rsplit('.', 1)[-1].lower() if '.' in csvFile.filename else ''
        if csv_ext != 'csv':
            return {"error": f"CSV附属文件仅支持.csv格式，当前格式: {csv_ext}", "detail": "invalid csv type"}
        csv_bytes = await csvFile.read()
        try:
            csv_content = csv_bytes.decode('utf-8')
        except UnicodeDecodeError:
            try:
                csv_content = csv_bytes.decode('gbk')
            except UnicodeDecodeError:
                csv_content = csv_bytes.decode('utf-8', errors='replace')
        file_type = 'md+csv'

    conn, cur = db
    name = docName.strip() if docName.strip() else file.filename.replace(f'.{ext}', '')
    user_id = int(current_user.get("userId", 1)) or 1
    user_name = current_user.get("username", "admin") or "admin"

    eff_date = None
    if effectiveDate:
        try:
            from datetime import datetime as dt
            eff_date = dt.strptime(effectiveDate, "%Y-%m-%d").date()
        except ValueError:
            eff_date = None

    await cur.execute(
        """INSERT INTO kb_md_documents 
           (source_doc_id, doc_name, category, description, content, csv_content, file_type,
            region, issuer, effective_date, doc_version,
            import_status, contributor_id, contributor_name, source_upload_time)
           VALUES (0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'pending', %s, %s, NOW())""",
        (name, category, description, content, csv_content, file_type,
         region or None, issuer or None, eff_date, docVersion or None,
         user_id, user_name),
    )
    new_id = cur.lastrowid

    return {
        "success": True,
        "id": new_id,
        "message": "文档上传成功",
        "data": {
            "id": new_id,
            "docName": name,
            "category": category,
            "fileType": file_type,
            "contentLength": len(content),
            "hasCsv": csv_content is not None,
            "region": region,
            "issuer": issuer,
        }
    }


@router.get("/kb/md-documents/chunk-strategies")
async def list_chunk_strategies(request: Request):
    _, err = _check_admin(request)
    if err:
        return err
    return {"strategies": STRATEGY_LIST}


@router.get("/kb/md-documents/check-prerequisites")
async def check_import_prerequisites(request: Request, db=Depends(get_db)):
    _, err = _check_admin(request)
    if err:
        return err

    issues = []

    try:
        embed_api_key = settings.EMBEDDING_API_KEY
        embed_model = settings.EMBEDDING_MODEL
        if not embed_api_key:
            issues.append({"type": "embedding", "message": "未配置Embedding模型API Key"})
        elif not embed_model:
            issues.append({"type": "embedding", "message": "未配置Embedding模型名称"})
    except Exception as e:
        issues.append({"type": "embedding", "message": f"获取Embedding配置失败: {str(e)}"})

    try:
        rerank_model = settings.RERANK_MODEL
        if not rerank_model:
            issues.append({"type": "rerank", "message": "未配置Rerank模型（可选）", "level": "warning"})
    except Exception:
        pass

    try:
        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:
                issues.append({"type": "qdrant", "message": f"无法连接Qdrant服务: HTTP {res.status_code}"})
            else:
                collections = res.json().get("result", {}).get("collections", [])
                if not collections:
                    issues.append({"type": "qdrant", "message": "Qdrant中没有可用集合"})
    except Exception as e:
        issues.append({"type": "qdrant", "message": f"连接Qdrant失败: {str(e)}"})

    return {
        "ready": len(issues) == 0,
        "issues": issues,
    }


@router.get("/kb/md-documents/{doc_id}")
async def get_md_document(doc_id: int, request: Request, db=Depends(get_db)):
    _, err = _check_admin(request)
    if err:
        return err

    conn, cur = db
    await cur.execute(
        """SELECT id, source_doc_id, doc_name, category, description, content,
                  import_status, wiki_page_id, qdrant_collection, qdrant_point_ids,
                  graphiti_imported, contributor_id, contributor_name,
                  source_upload_time, remark, created_at, updated_at
           FROM kb_md_documents WHERE id = %s AND deleted_at IS NULL""",
        (doc_id,),
    )
    row = await cur.fetchone()
    if not row:
        return {"error": "MD文档不存在", "detail": "document not found"}

    r = dict(row)
    return {
        "id": r["id"],
        "sourceDocId": r["source_doc_id"],
        "docName": r["doc_name"],
        "category": r["category"],
        "description": r["description"] or "",
        "content": r["content"] or "",
        "importStatus": r["import_status"],
        "wikiPageId": r["wiki_page_id"] or "",
        "qdrantCollection": r["qdrant_collection"] or "",
        "qdrantPointIds": json.loads(r["qdrant_point_ids"]) if r.get("qdrant_point_ids") else [],
        "graphitiImported": bool(r.get("graphiti_imported", 0)),
        "contributorId": r["contributor_id"],
        "contributorName": r["contributor_name"],
        "sourceUploadTime": str(r["source_upload_time"]) if r["source_upload_time"] else "",
        "remark": r["remark"] or "",
        "createdAt": str(r["created_at"]) if r["created_at"] else "",
        "updatedAt": str(r["updated_at"]) if r["updated_at"] else "",
    }


@router.put("/kb/md-documents/{doc_id}")
async def update_md_document(doc_id: int, request: Request, db=Depends(get_db)):
    _, err = _check_admin(request)
    if err:
        return err

    conn, cur = db
    body = await request.json()

    fields = []
    params = []

    if "docName" in body:
        fields.append("doc_name = %s")
        params.append(body["docName"])
    if "description" in body:
        fields.append("description = %s")
        params.append(body["description"])
    if "content" in body:
        fields.append("content = %s")
        params.append(body["content"])
    if "remark" in body:
        fields.append("remark = %s")
        params.append(body["remark"])
    if "csvContent" in body:
        fields.append("csv_content = %s")
        params.append(body["csvContent"])
        if body["csvContent"]:
            fields.append("file_type = %s")
            params.append("md+csv")
        else:
            fields.append("file_type = %s")
            params.append("md")
    if "region" in body:
        fields.append("region = %s")
        params.append(body["region"] or None)
    if "issuer" in body:
        fields.append("issuer = %s")
        params.append(body["issuer"] or None)
    if "effectiveDate" in body:
        fields.append("effective_date = %s")
        params.append(body["effectiveDate"] or None)
    if "docVersion" in body:
        fields.append("doc_version = %s")
        params.append(body["docVersion"] or None)

    if not fields:
        return {"error": "没有要更新的字段", "detail": "nothing to update"}

    params.append(doc_id)
    await cur.execute(
        f"UPDATE kb_md_documents SET {', '.join(fields)}, updated_at = NOW() WHERE id = %s AND deleted_at IS NULL",
        tuple(params),
    )

    if cur.rowcount == 0:
        return {"error": "MD文档不存在", "detail": "not found"}

    return {"success": True, "message": "更新成功"}


@router.delete("/kb/md-documents/{doc_id}")
async def delete_md_document(doc_id: int, request: Request, db=Depends(get_db)):
    _, err = _check_admin(request)
    if err:
        return err

    conn, cur = db
    await cur.execute(
        "UPDATE kb_md_documents SET deleted_at = NOW() WHERE id = %s AND deleted_at IS NULL",
        (doc_id,),
    )

    if cur.rowcount == 0:
        return {"error": "MD文档不存在", "detail": "not found"}

    return {"success": True, "message": "删除成功"}


@router.post("/kb/md-documents/batch-delete")
async def batch_delete_md_documents(request: Request, db=Depends(get_db)):
    _, err = _check_admin(request)
    if err:
        return err

    body = await request.json()
    ids = body.get("ids", [])
    if not ids:
        return {"error": "请选择要删除的文档", "detail": "ids required"}

    conn, cur = db
    placeholders = ','.join(['%s'] * len(ids))
    await cur.execute(
        f"UPDATE kb_md_documents SET deleted_at = NOW() WHERE id IN ({placeholders}) AND deleted_at IS NULL",
        tuple(ids),
    )

    return {"success": True, "message": f"成功删除 {len(ids)} 个MD文档"}


@router.post("/kb/md-documents/{doc_id}/chunk-preview")
async def preview_chunks(doc_id: int, request: Request, db=Depends(get_db)):
    _, err = _check_admin(request)
    if err:
        return err

    body = await request.json()
    chunk_strategy = body.get("chunk_strategy", "auto")

    conn, cur = db
    await cur.execute(
        "SELECT content, csv_content, file_type, doc_name, category FROM kb_md_documents WHERE id = %s AND deleted_at IS NULL",
        (doc_id,),
    )
    row = await cur.fetchone()
    if not row:
        return {"error": "MD文档不存在", "detail": "document not found"}

    doc = dict(row)
    content = doc.get("content", "")
    csv_content = doc.get("csv_content")
    file_type = doc.get("file_type", "md")
    doc_name = doc.get("doc_name", "")
    category = doc.get("category", "other")

    chunk_content = csv_content if csv_content else content

    engine = ChunkEngine()
    if chunk_strategy == "auto":
        chunk_strategy = engine.auto_detect_strategy(chunk_content, category)

    preview = engine.preview(chunk_content, chunk_strategy, category, {
        "doc_id": doc_id,
        "doc_name": doc_name,
        "category": category,
    })

    return {
        "strategy": preview.strategy_used,
        "strategyDisplayName": preview.strategy_display_name,
        "totalChunks": preview.total_chunks,
        "estimatedQdrantPoints": preview.estimated_qdrant_points,
        "chunks": preview.chunks,
    }


@router.post("/kb/md-documents/{doc_id}/import")
async def import_md_document(doc_id: int, request: Request, db=Depends(get_db)):
    _, err = _check_admin(request)
    if err:
        return err

    body = await request.json()
    target_collection = body.get("target_collection", "")
    import_qdrant = body.get("import_qdrant", False)
    import_graphiti = body.get("import_graphiti", False)
    chunk_strategy = body.get("chunk_strategy", "auto")

    if not any([import_qdrant, import_graphiti]):
        return {"error": "请至少选择一个入库目标", "detail": "no import target selected"}

    conn, cur = db
    await cur.execute(
        "SELECT id, content, csv_content, file_type, doc_name, category, region, issuer, effective_date, doc_version FROM kb_md_documents WHERE id = %s AND deleted_at IS NULL",
        (doc_id,),
    )
    row = await cur.fetchone()
    if not row:
        return {"error": "MD文档不存在", "detail": "document not found"}

    doc = dict(row)
    content = doc.get("content", "")
    csv_content = doc.get("csv_content")
    file_type = doc.get("file_type", "md")
    doc_name = doc.get("doc_name", "")
    category = doc.get("category", "other")

    options = ImportOptions(
        target_collection=target_collection,
        import_qdrant=import_qdrant,
        import_graphiti=import_graphiti,
        chunk_strategy=chunk_strategy,
        csv_content=csv_content,
        region=doc.get("region") or "",
        issuer=doc.get("issuer") or "",
        effective_date=str(doc["effective_date"]) if doc.get("effective_date") else "",
        doc_version=doc.get("doc_version") or "",
    )

    importer = DocumentImporter()
    result = await importer.import_document(
        doc_id=doc_id,
        content=content,
        doc_name=doc_name,
        category=category,
        options=options,
        db=db,
    )

    return {
        "success": result.success,
        "message": "入库操作完成" if result.success else "入库部分失败",
        "results": {
            k: v for k, v in {
                "wiki_result": result.wiki_result,
                "qdrant_result": result.qdrant_result,
                "graphiti_result": result.graphiti_result,
            }.items() if v is not None
        },
        "chunk_info": result.chunk_info,
        "errors": result.errors,
    }


@router.post("/kb/md-documents/batch-import")
async def batch_import_md_documents(request: Request, db=Depends(get_db)):
    _, err = _check_admin(request)
    if err:
        return err

    body = await request.json()
    ids = body.get("ids", [])
    target_collection = body.get("target_collection", "")
    import_qdrant = body.get("import_qdrant", False)
    import_graphiti = body.get("import_graphiti", False)
    chunk_strategy = body.get("chunk_strategy", "auto")

    if not ids:
        return {"error": "请选择要入库的文档", "detail": "ids required"}

    results = {}
    success_count = 0
    failed_count = 0

    for doc_id in ids:
        try:
            async with httpx.AsyncClient(timeout=120) as client:
                import_res = await client.post(
                    f"http://localhost:{settings.BFF_API_PORT}/admin/kb/md-documents/{doc_id}/import",
                    json={
                        "target_collection": target_collection,
                        "import_qdrant": import_qdrant,
                        "import_graphiti": import_graphiti,
                        "chunk_strategy": chunk_strategy,
                    },
                )
                result = import_res.json()
                results[str(doc_id)] = result
                if result.get("success"):
                    success_count += 1
                else:
                    failed_count += 1
        except Exception as e:
            results[str(doc_id)] = {"error": str(e)}
            failed_count += 1

    return {
        "success": True,
        "message": f"批量入库完成: 成功 {success_count}, 失败 {failed_count}",
        "results": results,
    }
