"""Markdown知识库搜索工具 — 优先从数据库搜索，fallback到文件系统"""

import json
import logging
import os
import re
from typing import Optional

from app.agent.tools.decorator import tool

from app.core.crypto import read_text_file, is_encrypted
from app.core.database import get_db, get_system_db, _has_split_databases

logger = logging.getLogger(__name__)

# 知识库根路径，相对于项目根目录
LAWS_BASE_PATH = os.environ.get(
    "ZHISHIKU_PATH",
    os.path.join(os.path.dirname(__file__), "..", "..", "..", "data", "zhishiku"),
)

# 缓存：{path: {title, region, articles: [{header, content}]}}
_laws_cache: Optional[list[dict]] = None
_cache_lock = __import__("threading").Lock()


# ==================== 解析工具 ====================

def _parse_frontmatter(text: str) -> dict:
    """解析 YAML frontmatter"""
    match = re.match(r'^---\s*\n(.*?)\n---\s*\n', text, re.DOTALL)
    if not match or not match.group(1):
        return {}
    fm = {}
    for line in match.group(1).split('\n'):
        ci = line.find(':')
        if ci > 0:
            key = line[:ci].strip()
            val = line[ci + 1:].strip()
            if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
                val = val[1:-1]
            fm[key] = val
    return fm


def _strip_frontmatter(text: str) -> str:
    """移除 frontmatter，返回正文"""
    return re.sub(r'^---\s*\n.*?\n---\s*\n', '', text, count=1, flags=re.DOTALL).strip()


def _split_articles(body: str) -> list[dict]:
    """按中文法条编号（第X条）拆分正文

    Returns:
        [{"header": "第三十四条", "content": "完整法条内容"}, ...]
    """
    articles = []
    # 匹配 ### 第X条 或 ## 第X条 或纯文本 第X条
    pattern = re.compile(r'^(#{1,3}\s*)?(第[一二三四五六七八九十百千万零\d]+条)\s*(.*)$', re.MULTILINE)

    splits = list(pattern.finditer(body))
    if not splits:
        # 没有法条格式，返回整篇作为一个条目
        if body.strip():
            articles.append({"header": "全文", "content": body.strip()})
        return articles

    # 第X条之前的内容（如总则部分）
    preamble = body[:splits[0].start()].strip()
    if preamble:
        articles.append({"header": "总则", "content": preamble})

    for i, match in enumerate(splits):
        header = match.group(2)  # 第X条
        start = match.end()
        end = splits[i + 1].start() if i + 1 < len(splits) else len(body)
        content = body[start:end].strip()
        # 清理 Markdown 标记
        content = re.sub(r'^#{1,6}\s+', '', content, flags=re.MULTILINE)
        articles.append({"header": header, "content": content})

    return articles


# ==================== 加载知识库 ====================

async def _load_laws_from_db() -> Optional[list[dict]]:
    """从 kb_documents 数据库表加载法律文档，返回 None 表示数据库不可用"""
    try:
        async for conn, cur in (get_system_db() if _has_split_databases() else get_db()):
            await cur.execute("SELECT COUNT(*) as cnt FROM kb_documents")
            row = await cur.fetchone()
            if row:
                row = dict(row)
            if not row or row.get('cnt', 0) == 0:
                return None

            await cur.execute("SELECT title, region, category, file_path, content FROM kb_documents")
            rows = await cur.fetchall()

            results = []
            for raw_row in rows:
                r = dict(raw_row)
                content = r.get('content', '') or ''
                articles = _split_articles(content) if content else []
                results.append({
                    "path": r.get('file_path', '') or '',
                    "title": r.get('title', '') or '',
                    "region": r.get('region', '') or '',
                    "category": r.get('category', '') or '',
                    "articles": articles,
                })
            logger.info(f"Loaded {len(results)} law documents from database")
            return results
    except Exception as e:
        logger.warning(f"Failed to load from database: {e}")
        return None


async def _load_all_laws() -> list[dict]:
    """从数据库加载所有法律文档

    Returns:
        [{"path", "title", "region", "category", "articles": [{header, content}]}]
    """
    global _laws_cache

    with _cache_lock:
        if _laws_cache is not None:
            return _laws_cache

    # 从数据库加载
    db_results = await _load_laws_from_db()
    if db_results is not None:
        with _cache_lock:
            _laws_cache = db_results
        return db_results

    # 数据库不可用时返回空列表
    logger.warning("知识库数据库不可用，返回空列表")
    _laws_cache = []
    return []


def invalidate_cache() -> None:
    """清除知识库缓存（文件更新后调用）"""
    global _laws_cache
    with _cache_lock:
        _laws_cache = None


# ==================== 搜索工具 ====================

@tool
async def search_markdown_knowledge(
    query: str,
    region: str = "",
    top_k: int = 5,
) -> str:
    """搜索本地Markdown法律法规知识库，根据关键词查找法条和法规内容。

    适用于查找具体的法律条文、法规条款、裁量基准等内容。
    支持按关键词搜索标题和正文，支持区域过滤。

    Args:
        query: 搜索关键词，如"食品安全法"、"行政处罚"、"无证经营"
        region: 区域过滤（可选），如"新疆"、"福建"，为空则不过滤
        top_k: 返回结果数量，默认5条
    """
    laws = await _load_all_laws()
    if not laws:
        return "[知识库搜索] 知识库为空或路径不存在，请检查 ZHISHIKU_PATH 配置"

    kw = query.lower().strip()
    if not kw:
        return "[知识库搜索] 请提供搜索关键词"

    scored = []

    for law in laws:
        # Region filter
        if region:
            law_region = law.get("region", "")
            if law_region and law_region != "全国" and region not in law_region:
                continue

        title = law.get("title", "")
        title_lower = title.lower()
        score = 0.0
        matched_articles = []

        # Title match — 3x weight
        if kw in title_lower:
            score += 3.0

        # Search articles
        for article in law.get("articles", []):
            header = article.get("header", "")
            content = article.get("content", "")
            content_lower = content.lower()
            header_lower = header.lower()

            article_score = 0.0
            if kw in header_lower:
                article_score += 2.0
            if kw in content_lower:
                # Count keyword occurrences for relevance
                count = content_lower.count(kw)
                article_score += min(count * 0.3, 2.0)

            if article_score > 0:
                matched_articles.append({
                    "header": header,
                    "content": content[:500],  # truncate for context window
                    "score": article_score,
                })
                score += article_score

        if score > 0:
            # Sort matched articles by score
            matched_articles.sort(key=lambda x: x["score"], reverse=True)
            scored.append({
                "title": title,
                "category": law.get("category", ""),
                "region": law.get("region", ""),
                "score": score,
                "articles": matched_articles[:3],  # top 3 articles per law
            })

    # Sort by total score
    scored.sort(key=lambda x: x["score"], reverse=True)

    if not scored:
        return f"[知识库搜索] 未找到与「{query}」相关的法规内容"

    # Format results
    parts = []
    for i, item in enumerate(scored[:top_k], 1):
        region_str = f"（{item['region']}）" if item.get("region") else ""
        parts.append(f"【{i}】{item['title']}{region_str} [{item['category']}]")
        for art in item["articles"]:
            content_preview = art["content"][:300]
            if len(art["content"]) > 300:
                content_preview += "..."
            parts.append(f"  {art['header']}：{content_preview}")
        parts.append("")  # blank line separator

    return "\n".join(parts)
