import json
import sqlite3
import uuid
from datetime import datetime
from typing import Any


class BaseRepo:
    """所有 Repo 的基类"""

    def __init__(self, conn: sqlite3.Connection, project_dir: str = ""):
        self.conn = conn
        self.project_dir = project_dir

    def _now(self) -> str:
        return datetime.now().astimezone().isoformat()

    def _commit(self):
        """统一提交逻辑：事务中不提交，非事务中立即提交"""
        from szyjiyi.db.connection import _in_transaction
        if not _in_transaction:
            self.conn.commit()


class BaseMemoryRepo(BaseRepo):
    """记忆类 Repo 的基类（memories / user_memories）"""
    TABLE = ""
    VEC_TABLE = ""
    TAG_TABLE = ""

    def insert(self, content: str, tags: list[str], scope: str, session_id: int,
               embedding: list[float], dedup_threshold: float = 0.95,
               source: str = "manual") -> dict:
        """插入记忆，自动去重"""
        # 向量相似度去重
        similar = self._find_similar(embedding, dedup_threshold)
        if similar:
            mid = similar["id"]
            self.conn.execute(
                f"UPDATE {self.TABLE} SET content=?, tags=?, updated_at=?, session_id=? WHERE id=?",
                (content, json.dumps(tags, ensure_ascii=False), self._now(), session_id, mid)
            )
            # 更新标签关联表
            self.conn.execute(f"DELETE FROM {self.TAG_TABLE} WHERE memory_id=?", (mid,))
            for t in tags:
                self.conn.execute(f"INSERT OR IGNORE INTO {self.TAG_TABLE} (memory_id, tag) VALUES (?,?)", (mid, t))
            self._commit()
            return {"id": mid, "action": "updated"}

        # 插入新记忆
        mid = str(uuid.uuid4())[:12]
        now = self._now()
        emb_blob = json.dumps(embedding).encode("utf-8")

        insert_sql = f"INSERT INTO {self.TABLE} (id, content, tags, scope, source, session_id, created_at, updated_at"
        values_sql = "VALUES (?,?,?,?,?,?,?,?"
        params: list[Any] = [mid, content, json.dumps(tags, ensure_ascii=False), scope, source, session_id, now, now]

        if self.project_dir:
            insert_sql += ", project_dir"
            values_sql += ",?"
            params.append(self.project_dir)

        insert_sql += f") {values_sql})"
        self.conn.execute(insert_sql, params)

        # 插入向量
        vec_insert_sql = f"INSERT INTO {self.VEC_TABLE} (rowid, embedding) VALUES ((SELECT rowid FROM {self.TABLE} WHERE id=?), ?)"
        self.conn.execute(vec_insert_sql, (mid, emb_blob))

        # 插入标签关联
        for t in tags:
            self.conn.execute(f"INSERT OR IGNORE INTO {self.TAG_TABLE} (memory_id, tag) VALUES (?,?)", (mid, t))

        self._commit()
        return {"id": mid, "action": "created"}

    def _find_similar(self, embedding: list[float], threshold: float) -> dict | None:
        """查找相似记忆"""
        emb_blob = json.dumps(embedding).encode("utf-8")
        sql = f"""
            SELECT m.id, m.content, vec_distance_cosine(v.embedding, ?) as dist
            FROM {self.TABLE} m
            JOIN {self.VEC_TABLE} v ON m.rowid = v.rowid
        """
        params: list[Any] = [emb_blob]

        if self.project_dir:
            sql += " WHERE m.project_dir=?"
            params.append(self.project_dir)

        sql += " ORDER BY dist LIMIT 1"

        row = self.conn.execute(sql, params).fetchone()
        if row and row["dist"] <= (1 - threshold):
            return dict(row)
        return None

    def search(self, embedding: list[float], top_k: int = 5, tags: list[str] | None = None,
               source: str | None = None) -> list[dict]:
        """向量搜索"""
        emb_blob = json.dumps(embedding).encode("utf-8")
        sql = f"""
            SELECT m.*, vec_distance_cosine(v.embedding, ?) as distance
            FROM {self.TABLE} m
            JOIN {self.VEC_TABLE} v ON m.rowid = v.rowid
        """
        params: list[Any] = [emb_blob]
        conditions = []

        if self.project_dir:
            conditions.append("m.project_dir=?")
            params.append(self.project_dir)

        if tags:
            tag_placeholders = ",".join("?" * len(tags))
            conditions.append(f"m.id IN (SELECT memory_id FROM {self.TAG_TABLE} WHERE tag IN ({tag_placeholders}))")
            params.extend(tags)

        if source:
            conditions.append("m.source=?")
            params.append(source)

        if conditions:
            sql += " WHERE " + " AND ".join(conditions)

        sql += " ORDER BY distance LIMIT ?"
        params.append(top_k)

        return [dict(r) for r in self.conn.execute(sql, params).fetchall()]

    def delete(self, memory_id: str) -> bool:
        """删除记忆"""
        cursor = self.conn.execute(f"DELETE FROM {self.TABLE} WHERE id=?", (memory_id,))
        self._commit()
        return cursor.rowcount > 0

    def delete_batch(self, memory_ids: list[str]) -> int:
        """批量删除"""
        if not memory_ids:
            return 0
        placeholders = ",".join("?" * len(memory_ids))
        cursor = self.conn.execute(f"DELETE FROM {self.TABLE} WHERE id IN ({placeholders})", memory_ids)
        self._commit()
        return cursor.rowcount

    def get_tag_counts(self, **filters) -> dict[str, int]:
        """子类覆盖"""
        raise NotImplementedError

    def get_ids_with_tag(self, tag: str, **filters) -> list[dict]:
        """子类覆盖"""
        raise NotImplementedError
