"""Vault SQLite FTS5 sidecar — 仅 enhancement_active(fts) 时启用。"""

from __future__ import annotations

import re
import sqlite3
from pathlib import Path
from typing import Any

_TOKEN_RE = re.compile(r"[\w\u4e00-\u9fff]+", re.UNICODE)


def _tokenize_query(q: str) -> str:
    tokens = _TOKEN_RE.findall((q or "").strip().lower())
    if not tokens:
        return ""
    return " ".join(f'"{t}"*' for t in tokens)


class VaultFtsIndex:
    def __init__(self, home: Path) -> None:
        self._path = home / "vault" / "_fts.sqlite"

    def _connect(self) -> sqlite3.Connection:
        self._path.parent.mkdir(parents=True, exist_ok=True)
        conn = sqlite3.connect(self._path)
        conn.row_factory = sqlite3.Row
        return conn

    def ensure_schema(self) -> None:
        with self._connect() as conn:
            conn.execute(
                """
                CREATE VIRTUAL TABLE IF NOT EXISTS vault_fts USING fts5(
                    vault_id UNINDEXED,
                    rel_path UNINDEXED,
                    namespace UNINDEXED,
                    title,
                    body,
                    tokenize='unicode61'
                )
                """
            )
            conn.commit()

    def upsert(
        self,
        *,
        vault_id: str,
        rel_path: str,
        namespace: str,
        title: str,
        body: str,
    ) -> None:
        self.ensure_schema()
        with self._connect() as conn:
            conn.execute("DELETE FROM vault_fts WHERE vault_id = ?", (vault_id,))
            conn.execute(
                "INSERT INTO vault_fts(vault_id, rel_path, namespace, title, body) VALUES (?,?,?,?,?)",
                (vault_id, rel_path, namespace, title, (body or "")[:4000]),
            )
            conn.commit()

    def remove(self, vault_id: str) -> None:
        if not self._path.is_file():
            return
        with self._connect() as conn:
            conn.execute("DELETE FROM vault_fts WHERE vault_id = ?", (vault_id,))
            conn.commit()

    def search(
        self,
        q: str,
        *,
        namespace: str,
        limit: int = 20,
    ) -> list[dict[str, Any]]:
        needle = _tokenize_query(q)
        if not needle:
            return []
        self.ensure_schema()
        with self._connect() as conn:
            rows = conn.execute(
                """
                SELECT vault_id, rel_path, namespace, title, snippet(vault_fts, 4, '', '', '…', 8) AS snippet
                FROM vault_fts
                WHERE namespace = ? AND vault_fts MATCH ?
                LIMIT ?
                """,
                (namespace, needle, limit),
            ).fetchall()
        return [
            {
                "vault_id": str(r["vault_id"]),
                "rel_path": str(r["rel_path"]),
                "title": str(r["title"] or ""),
                "snippet": str(r["snippet"] or "").replace("\n", " ").strip(),
                "score": 1.0,
            }
            for r in rows
        ]

    def rebuild_from_store(self, store) -> int:
        """全量重建索引；返回写入条数。"""
        self.ensure_schema()
        with self._connect() as conn:
            conn.execute("DELETE FROM vault_fts")
            conn.commit()
        count = 0
        for entry in store.load_index().get("entries") or []:
            if not isinstance(entry, dict):
                continue
            rel = str(entry.get("rel_path") or "")
            vid = str(entry.get("vault_id") or "")
            if not rel or not vid:
                continue
            try:
                doc = store.read_document(rel)
            except (FileNotFoundError, OSError):
                continue
            fm = doc.get("frontmatter") or {}
            self.upsert(
                vault_id=vid,
                rel_path=rel,
                namespace=str(fm.get("namespace") or entry.get("namespace") or "user"),
                title=str(fm.get("title") or entry.get("title") or ""),
                body=str(doc.get("body") or ""),
            )
            count += 1
        return count
