"""SimHash deduplication — block near-duplicate memories before ingest."""

import hashlib
import sqlite3
import time

_MASK64 = (1 << 64) - 1


def _to_signed64(n: int) -> int:
    """Convert unsigned 64-bit int to signed for SQLite storage."""
    n &= _MASK64
    if n >= (1 << 63):
        return n - (1 << 64)
    return n


def _to_unsigned64(n: int) -> int:
    """Convert signed SQLite int back to unsigned."""
    return n & _MASK64


def simhash(text: str, hashbits: int = 64) -> int:
    """Compute SimHash of text using character 3-gram shingles."""
    text = text.lower()
    if len(text) < 3:
        return int(hashlib.md5(text.encode()).hexdigest(), 16) & ((1 << hashbits) - 1)

    shingles = [text[i : i + 3] for i in range(len(text) - 2)]

    v = [0] * hashbits
    for shingle in shingles:
        h = int(hashlib.md5(shingle.encode()).hexdigest(), 16)
        for i in range(hashbits):
            if h & (1 << i):
                v[i] += 1
            else:
                v[i] -= 1

    result = 0
    for i in range(hashbits):
        if v[i] > 0:
            result |= 1 << i
    return result


def hamming_distance(a: int, b: int) -> int:
    """Count differing bits between two integers."""
    return bin(a ^ b).count("1")


class DedupStore:
    """SQLite-backed SimHash store for deduplication."""

    def __init__(self, conn: sqlite3.Connection, threshold: int = 10):
        self._conn = conn
        self._threshold = threshold
        self._conn.execute(
            "CREATE TABLE IF NOT EXISTS simhashes (hash INTEGER, memory_id TEXT, timestamp REAL)"
        )
        self._conn.commit()

    def is_duplicate(self, text: str) -> bool:
        """Check if text is a near-duplicate of any stored hash."""
        h = simhash(text)
        rows = self._conn.execute("SELECT hash FROM simhashes").fetchall()
        for (stored_hash,) in rows:
            if hamming_distance(h, _to_unsigned64(stored_hash)) <= self._threshold:
                return True
        return False

    def clear(self) -> int:
        """Delete all hashes."""
        n = self._conn.execute("SELECT COUNT(*) FROM simhashes").fetchone()[0]
        self._conn.execute("DELETE FROM simhashes")
        self._conn.commit()
        return n

    def add(self, text: str, memory_id: str = "") -> int:
        """Store SimHash of text. Returns hash value."""
        h = simhash(text)
        self._conn.execute(
            "INSERT INTO simhashes (hash, memory_id, timestamp) VALUES (?, ?, ?)",
            (_to_signed64(h), memory_id, time.time()),
        )
        self._conn.commit()
        return h
