"""LLMHosts Tier 2 cache -- vCache verified semantic cache (ICLR 2026).

Key insight: instead of one global similarity threshold (GPTCache style),
vCache learns a **separate** adaptive threshold per cached embedding using
online logistic regression.

The user sets a maximum error rate (``delta`` parameter, default 0.01 = 1%).
The algorithm mathematically guarantees the actual false-positive rate stays
below ``delta``.

Thresholds adapt over time: frequently-hit embeddings with clear semantic
boundaries get tighter thresholds; rare edge-case embeddings get looser
ones.

Requires the ``[smart]`` pip tier::

    pip install llmhosts[smart]
"""

from __future__ import annotations

import hashlib
import logging
import math
from datetime import datetime, timedelta, timezone
from typing import TYPE_CHECKING, Any

import aiosqlite
from pydantic import BaseModel, ConfigDict

if TYPE_CHECKING:
    from pathlib import Path

logger = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# Optional dependency guard
# ---------------------------------------------------------------------------

VCACHE_AVAILABLE = False
try:
    import numpy as np

    VCACHE_AVAILABLE = True
except ImportError:  # pragma: no cover
    np = None  # type: ignore[assignment]

# ---------------------------------------------------------------------------
# Pydantic models
# ---------------------------------------------------------------------------


class AdaptiveThreshold(BaseModel):
    """Per-entry adaptive threshold state for the online logistic regression."""

    model_config = ConfigDict(frozen=False)

    cache_key: str
    threshold: float  # current similarity threshold for this entry
    true_positives: int = 0
    false_positives: int = 0
    total_queries: int = 0
    last_updated: datetime | None = None

    # Online logistic regression state
    weight: float = 0.0  # logistic regression weight (w in log-loss)
    learning_rate: float = 0.01


class VCacheResult(BaseModel):
    """Result of a verified cache hit."""

    model_config = ConfigDict(frozen=True)

    cache_key: str
    response_json: str
    similarity: float
    threshold: float
    model: str
    namespace: str | None
    cached_at: datetime


class VCacheStats(BaseModel):
    """Aggregate statistics for the vCache layer."""

    total_entries: int
    total_queries: int
    total_hits: int
    total_misses: int
    hit_rate: float
    eviction_count: int = 0
    empirical_fp_rate: float
    delta_target: float
    avg_threshold: float
    min_threshold: float
    max_threshold: float
    threshold_distribution: dict[str, int]  # bucketed by 0.05 ranges


# ---------------------------------------------------------------------------
# SQL schema
# ---------------------------------------------------------------------------

_CREATE_ENTRIES_TABLE = """
CREATE TABLE IF NOT EXISTS vcache_entries (
    cache_key       TEXT PRIMARY KEY,
    model           TEXT NOT NULL,
    namespace       TEXT,
    messages_hash   TEXT NOT NULL,
    response_json   TEXT NOT NULL,
    embedding_blob  BLOB NOT NULL,
    created_at      TEXT NOT NULL,
    expires_at      TEXT NOT NULL,
    hit_count       INTEGER NOT NULL DEFAULT 0,
    last_hit_at     TEXT,
    size_bytes      INTEGER NOT NULL
);
"""

_CREATE_THRESHOLDS_TABLE = """
CREATE TABLE IF NOT EXISTS vcache_thresholds (
    cache_key        TEXT PRIMARY KEY,
    threshold        REAL NOT NULL,
    true_positives   INTEGER NOT NULL DEFAULT 0,
    false_positives  INTEGER NOT NULL DEFAULT 0,
    total_queries    INTEGER NOT NULL DEFAULT 0,
    weight           REAL NOT NULL DEFAULT 0.0,
    learning_rate    REAL NOT NULL DEFAULT 0.01,
    last_updated     TEXT,
    FOREIGN KEY (cache_key) REFERENCES vcache_entries(cache_key) ON DELETE CASCADE
);
"""

_CREATE_GLOBAL_STATS_TABLE = """
CREATE TABLE IF NOT EXISTS vcache_global_stats (
    id              INTEGER PRIMARY KEY CHECK (id = 1),
    total_queries   INTEGER NOT NULL DEFAULT 0,
    total_hits      INTEGER NOT NULL DEFAULT 0,
    total_misses    INTEGER NOT NULL DEFAULT 0,
    total_fp        INTEGER NOT NULL DEFAULT 0
);
"""

_CREATE_INDEXES = [
    "CREATE INDEX IF NOT EXISTS idx_vc_model ON vcache_entries (model);",
    "CREATE INDEX IF NOT EXISTS idx_vc_namespace ON vcache_entries (namespace);",
    "CREATE INDEX IF NOT EXISTS idx_vc_expires ON vcache_entries (expires_at);",
    "CREATE INDEX IF NOT EXISTS idx_vc_msghash ON vcache_entries (messages_hash);",
]


# ---------------------------------------------------------------------------
# vCache implementation
# ---------------------------------------------------------------------------


class VCache:
    """Tier 2: vCache verified semantic cache (ICLR 2026).

    Instead of a single global similarity threshold, each cached embedding
    maintains its own adaptive threshold via online logistic regression.  The
    ``delta`` parameter caps the empirical false-positive rate.
    """

    def __init__(
        self,
        db_path: Path,
        embedding_dim: int = 384,
        delta: float = 0.01,
        initial_threshold: float = 0.85,
        ttl_seconds: int = 3600,
        max_bytes: int = 256 * 1024 * 1024,
    ) -> None:
        self._db_path = db_path
        self._dim = embedding_dim
        self._delta = delta  # max acceptable false-positive rate
        self._initial_threshold = initial_threshold
        self._ttl_seconds = ttl_seconds
        self._max_bytes = max_bytes
        self._db: aiosqlite.Connection | None = None
        self._thresholds: dict[str, AdaptiveThreshold] = {}
        self._evictions = 0

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    async def initialize(self) -> None:
        """Create vCache tables and load existing thresholds into memory."""
        self._db_path.parent.mkdir(parents=True, exist_ok=True)
        self._db = await aiosqlite.connect(str(self._db_path))
        await self._db.execute("PRAGMA journal_mode=WAL;")
        await self._db.execute("PRAGMA synchronous=NORMAL;")
        await self._db.execute("PRAGMA foreign_keys=ON;")
        await self._db.execute(_CREATE_ENTRIES_TABLE)
        await self._db.execute(_CREATE_THRESHOLDS_TABLE)
        await self._db.execute(_CREATE_GLOBAL_STATS_TABLE)
        for idx_sql in _CREATE_INDEXES:
            await self._db.execute(idx_sql)
        # Ensure singleton global stats row
        await self._db.execute(
            "INSERT OR IGNORE INTO vcache_global_stats (id, total_queries, total_hits, total_misses, total_fp) "
            "VALUES (1, 0, 0, 0, 0)",
        )
        await self._db.commit()

        # Load thresholds into memory for fast lookup
        await self._load_thresholds()
        logger.info(
            "VCache initialised (db=%s, dim=%d, delta=%.4f, initial_threshold=%.2f, entries=%d)",
            self._db_path,
            self._dim,
            self._delta,
            self._initial_threshold,
            len(self._thresholds),
        )

    async def close(self) -> None:
        """Persist thresholds and close database."""
        if self._db is not None:
            await self._flush_thresholds()
            await self._db.close()
            self._db = None
            logger.debug("VCache closed")

    def _ensure_db(self) -> aiosqlite.Connection:
        if self._db is None:
            raise RuntimeError("VCache not initialised -- call initialize() first")
        return self._db

    # ------------------------------------------------------------------
    # Threshold persistence
    # ------------------------------------------------------------------

    async def _load_thresholds(self) -> None:
        """Load all adaptive thresholds from SQLite into memory."""
        db = self._ensure_db()
        self._thresholds.clear()
        async with db.execute(
            "SELECT cache_key, threshold, true_positives, false_positives, "
            "total_queries, weight, learning_rate, last_updated "
            "FROM vcache_thresholds",
        ) as cur:
            async for row in cur:
                self._thresholds[row[0]] = AdaptiveThreshold(
                    cache_key=row[0],
                    threshold=row[1],
                    true_positives=row[2],
                    false_positives=row[3],
                    total_queries=row[4],
                    weight=row[5],
                    learning_rate=row[6],
                    last_updated=datetime.fromisoformat(row[7]) if row[7] else None,
                )

    async def _flush_thresholds(self) -> None:
        """Persist all in-memory thresholds to SQLite."""
        db = self._ensure_db()
        for at in self._thresholds.values():
            await db.execute(
                "INSERT OR REPLACE INTO vcache_thresholds "
                "(cache_key, threshold, true_positives, false_positives, "
                "total_queries, weight, learning_rate, last_updated) "
                "VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
                (
                    at.cache_key,
                    at.threshold,
                    at.true_positives,
                    at.false_positives,
                    at.total_queries,
                    at.weight,
                    at.learning_rate,
                    at.last_updated.isoformat() if at.last_updated else None,
                ),
            )
        await db.commit()

    async def _persist_threshold(self, at: AdaptiveThreshold) -> None:
        """Persist a single threshold entry."""
        db = self._ensure_db()
        await db.execute(
            "INSERT OR REPLACE INTO vcache_thresholds "
            "(cache_key, threshold, true_positives, false_positives, "
            "total_queries, weight, learning_rate, last_updated) "
            "VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
            (
                at.cache_key,
                at.threshold,
                at.true_positives,
                at.false_positives,
                at.total_queries,
                at.weight,
                at.learning_rate,
                at.last_updated.isoformat() if at.last_updated else None,
            ),
        )
        await db.commit()

    # ------------------------------------------------------------------
    # Internal helpers
    # ------------------------------------------------------------------

    @staticmethod
    def _now() -> datetime:
        return datetime.now(timezone.utc)

    @staticmethod
    def _iso(dt: datetime) -> str:
        return dt.isoformat()

    @staticmethod
    def _cosine_similarity(a: Any, b: Any) -> float:
        """Cosine similarity between two numpy arrays."""
        if np is None:
            return 0.0
        a_arr = np.asarray(a, dtype=np.float32).ravel()
        b_arr = np.asarray(b, dtype=np.float32).ravel()
        dot = float(np.dot(a_arr, b_arr))
        norm = float(np.linalg.norm(a_arr) * np.linalg.norm(b_arr))
        if norm < 1e-12:
            return 0.0
        return dot / norm

    @staticmethod
    def _sigmoid(x: float) -> float:
        """Numerically stable sigmoid function."""
        if x >= 0:
            return 1.0 / (1.0 + math.exp(-x))
        exp_x = math.exp(x)
        return exp_x / (1.0 + exp_x)

    def _compute_adaptive_threshold(self, at: AdaptiveThreshold) -> float:
        """Compute the effective threshold from logistic regression weight.

        The logistic regression maps a similarity score to a probability of
        correct match.  The threshold is set such that
        P(correct | similarity >= threshold) >= 1 - delta.

        Derivation:
          sigmoid(w * threshold + b) >= 1 - delta
          w * threshold + b >= logit(1 - delta)

        We use the weight as a single-parameter model (bias absorbed into
        initial_threshold) and translate back to a similarity threshold.
        """
        # Clamp delta to avoid log(0)
        delta_clamped = max(self._delta, 1e-8)
        # logit(1 - delta) = log((1 - delta) / delta)
        logit_target = math.log((1.0 - delta_clamped) / delta_clamped)

        if abs(at.weight) < 1e-8:
            # No data yet -- use initial threshold
            return self._initial_threshold

        # threshold = (logit_target - bias) / weight
        # We model: sigmoid(weight * (sim - initial_threshold)) >= 1 - delta
        # So: weight * (threshold - initial_threshold) >= logit_target
        # threshold = initial_threshold + logit_target / weight
        raw_threshold = self._initial_threshold + logit_target / at.weight

        # Clamp to sensible range [0.5, 0.99]
        return max(0.5, min(0.99, raw_threshold))

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    async def get(
        self,
        embedding: Any,
        model: str,
        namespace: str | None = None,
    ) -> VCacheResult | None:
        """Look up semantic cache match.

        1. Find nearest cached embedding (cosine similarity)
        2. Check if similarity exceeds the ADAPTIVE threshold for that entry
        3. If yes, return cached response (verified match)
        4. If no, return None (miss)
        """
        if np is None:
            return None

        db = self._ensure_db()
        now = self._now()
        query_emb = np.asarray(embedding, dtype=np.float32).ravel()

        # Build query for candidates
        if namespace is not None:
            sql = (
                "SELECT cache_key, model, namespace, messages_hash, response_json, "
                "embedding_blob, created_at, expires_at, hit_count, size_bytes "
                "FROM vcache_entries "
                "WHERE model = ? AND namespace = ? AND expires_at > ?"
            )
            params: tuple[Any, ...] = (model.strip().lower(), namespace, self._iso(now))
        else:
            sql = (
                "SELECT cache_key, model, namespace, messages_hash, response_json, "
                "embedding_blob, created_at, expires_at, hit_count, size_bytes "
                "FROM vcache_entries "
                "WHERE model = ? AND expires_at > ?"
            )
            params = (model.strip().lower(), self._iso(now))

        async with db.execute(sql, params) as cur:
            rows = await cur.fetchall()

        if not rows:
            await self._record_miss(db)
            return None

        # Find best match
        best_row: aiosqlite.Row | None = None
        best_sim = -1.0
        for row in rows:
            cached_emb = np.frombuffer(row[5], dtype=np.float32)
            sim = self._cosine_similarity(query_emb, cached_emb)
            if sim > best_sim:
                best_sim = sim
                best_row = row

        if best_row is None:
            await self._record_miss(db)
            return None

        # Check adaptive threshold for the best match
        cache_key = best_row[0]
        at = self._thresholds.get(cache_key)
        effective_threshold = self._initial_threshold if at is None else at.threshold

        if best_sim < effective_threshold:
            # Below threshold -- miss
            await self._record_miss(db)
            logger.debug(
                "VCache MISS (sim=%.4f < threshold=%.4f) key=%s",
                best_sim,
                effective_threshold,
                cache_key[:12],
            )
            return None

        # Verified hit!
        new_hits = best_row[8] + 1
        await db.execute(
            "UPDATE vcache_entries SET hit_count = ?, last_hit_at = ? WHERE cache_key = ?",
            (new_hits, self._iso(now), cache_key),
        )
        await self._record_hit(db)

        # Increment query count on threshold
        if at is not None:
            at.total_queries += 1
            at.last_updated = now

        logger.debug(
            "VCache HIT (sim=%.4f, threshold=%.4f) key=%s",
            best_sim,
            effective_threshold,
            cache_key[:12],
        )

        return VCacheResult(
            cache_key=cache_key,
            response_json=best_row[4],
            similarity=round(best_sim, 6),
            threshold=round(effective_threshold, 6),
            model=best_row[1],
            namespace=best_row[2],
            cached_at=datetime.fromisoformat(best_row[6]),
        )

    async def put(
        self,
        embedding: Any,
        model: str,
        messages_hash: str,
        response_json: str,
        namespace: str | None = None,
    ) -> str:
        """Store new cache entry with initial threshold."""
        if np is None:
            raise RuntimeError("VCache requires numpy -- install llmhost[smart]")

        db = self._ensure_db()
        now = self._now()
        expires_at = now + timedelta(seconds=self._ttl_seconds)

        emb_arr = np.asarray(embedding, dtype=np.float32).ravel()
        emb_blob = emb_arr.tobytes()

        # Deterministic cache key from model + messages_hash + namespace
        key_input = f"{model.strip().lower()}:{messages_hash}:{namespace or ''}"
        cache_key = hashlib.sha256(key_input.encode("utf-8")).hexdigest()

        size_bytes = len(response_json.encode("utf-8"))

        await db.execute(
            "INSERT OR REPLACE INTO vcache_entries "
            "(cache_key, model, namespace, messages_hash, response_json, "
            "embedding_blob, created_at, expires_at, hit_count, last_hit_at, size_bytes) "
            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, NULL, ?)",
            (
                cache_key,
                model.strip().lower(),
                namespace,
                messages_hash,
                response_json,
                emb_blob,
                self._iso(now),
                self._iso(expires_at),
                size_bytes,
            ),
        )

        # Create initial adaptive threshold
        at = AdaptiveThreshold(
            cache_key=cache_key,
            threshold=self._initial_threshold,
            weight=10.0,  # Start with moderate positive weight
            learning_rate=0.01,
            last_updated=now,
        )
        self._thresholds[cache_key] = at
        await self._persist_threshold(at)

        await self._evict_if_needed()
        logger.debug("VCache PUT key=%s model=%s ns=%s", cache_key[:12], model, namespace)
        return cache_key

    async def _evict_if_needed(self) -> None:
        """Evict least-recently-used entries when total size exceeds max_bytes."""
        db = self._ensure_db()

        async with db.execute(
            "SELECT COALESCE(SUM(size_bytes + LENGTH(embedding_blob)), 0) FROM vcache_entries",
        ) as cur:
            row = await cur.fetchone()
            total_bytes = row[0] if row else 0

        if total_bytes <= self._max_bytes:
            return

        evicted = 0
        while total_bytes > self._max_bytes:
            async with db.execute(
                "SELECT cache_key, size_bytes + LENGTH(embedding_blob) as entry_bytes "
                "FROM vcache_entries "
                "ORDER BY COALESCE(last_hit_at, '1970-01-01') ASC, created_at ASC "
                "LIMIT 50",
            ) as cur:
                rows = await cur.fetchall()

            if not rows:
                break

            keys = [r[0] for r in rows]
            batch_bytes = sum(r[1] for r in rows)
            placeholders = ",".join("?" for _ in keys)
            # CASCADE will delete vcache_thresholds rows too
            await db.execute(f"DELETE FROM vcache_entries WHERE cache_key IN ({placeholders})", keys)  # nosec B608
            # Also clean up in-memory thresholds
            for k in keys:
                self._thresholds.pop(k, None)
            total_bytes -= batch_bytes
            evicted += len(keys)

        if evicted > 0:
            self._evictions += evicted
            await db.commit()
            try:
                from llmhosts.metrics.prometheus import record_cache_eviction

                record_cache_eviction("vcache", evicted)
            except ImportError:
                pass
            logger.info("Evicted %d vCache entries (max_bytes=%d)", evicted, self._max_bytes)

    async def update_threshold(
        self,
        cache_key: str,
        was_correct: bool,
        similarity: float,
    ) -> None:
        """Online logistic regression update for this entry's threshold.

        Performs a single step of online gradient descent on the logistic loss
        to update the per-entry weight, then recomputes the threshold such
        that the false-positive rate target (delta) is maintained.

        Loss function per sample:
            L = -[y * log(p) + (1-y) * log(1-p)]
        where:
            y = 1 if was_correct else 0
            p = sigmoid(w * (similarity - initial_threshold))

        Gradient:
            dL/dw = -(y - p) * (similarity - initial_threshold)

        Update:
            w := w - lr * dL/dw

        After updating w, recompute threshold from the logistic model
        such that P(correct | sim >= threshold) >= 1 - delta.
        """
        at = self._thresholds.get(cache_key)
        if at is None:
            logger.warning("update_threshold: unknown cache_key=%s", cache_key[:12])
            return

        now = self._now()

        # Label
        y = 1.0 if was_correct else 0.0

        # Feature: centered similarity
        x = similarity - self._initial_threshold

        # Forward pass: predicted probability
        p = self._sigmoid(at.weight * x)

        # Gradient of logistic loss w.r.t. weight
        # dL/dw = -(y - p) * x
        grad = -(y - p) * x

        # Gradient descent step
        at.weight -= at.learning_rate * grad

        # Clamp weight to prevent divergence
        at.weight = max(-50.0, min(50.0, at.weight))

        # Update counters
        at.total_queries += 1
        if was_correct:
            at.true_positives += 1
        else:
            at.false_positives += 1

        # Recompute threshold from updated weight
        at.threshold = self._compute_adaptive_threshold(at)
        at.last_updated = now

        # Update global FP stats for delta enforcement
        if not was_correct:
            db = self._ensure_db()
            await db.execute(
                "UPDATE vcache_global_stats SET total_fp = total_fp + 1 WHERE id = 1",
            )
            await db.commit()

        # Check global FP rate against delta -- emergency tightening
        await self._enforce_delta(at)

        # Persist updated threshold
        await self._persist_threshold(at)

        logger.debug(
            "VCache THRESHOLD UPDATE key=%s correct=%s sim=%.4f new_threshold=%.4f weight=%.4f",
            cache_key[:12],
            was_correct,
            similarity,
            at.threshold,
            at.weight,
        )

    async def _enforce_delta(self, at: AdaptiveThreshold) -> None:
        """If empirical FP rate exceeds delta, tighten this entry's threshold.

        This provides a global safety net: even if the online logistic
        regression hasn't converged, we ensure the cache doesn't exceed
        the user's requested error budget.
        """
        fp_rate = await self.get_fp_rate()
        if fp_rate > self._delta and at.total_queries > 0:
            # Proportional tightening: raise threshold by the excess FP ratio
            excess = fp_rate - self._delta
            tighten = min(excess * 2.0, 0.05)  # cap single-step tightening at 0.05
            at.threshold = min(0.99, at.threshold + tighten)
            logger.info(
                "VCache DELTA ENFORCEMENT: fp_rate=%.4f > delta=%.4f, tightened key=%s threshold to %.4f",
                fp_rate,
                self._delta,
                at.cache_key[:12],
                at.threshold,
            )

    async def get_fp_rate(self) -> float:
        """Calculate current empirical false-positive rate across all entries."""
        db = self._ensure_db()
        async with db.execute(
            "SELECT total_hits, total_fp FROM vcache_global_stats WHERE id = 1",
        ) as cur:
            row = await cur.fetchone()

        if row is None or row[0] == 0:
            return 0.0

        total_hits = row[0]
        total_fp = row[1]
        return total_fp / total_hits if total_hits > 0 else 0.0

    async def stats(self) -> VCacheStats:
        """Get cache statistics including threshold distribution."""
        db = self._ensure_db()

        # Global counters
        async with db.execute(
            "SELECT total_queries, total_hits, total_misses, total_fp FROM vcache_global_stats WHERE id = 1",
        ) as cur:
            row = await cur.fetchone()
            total_queries = row[0] if row else 0
            total_hits = row[1] if row else 0
            total_misses = row[2] if row else 0
            total_fp = row[3] if row else 0

        # Entry count
        async with db.execute("SELECT COUNT(*) FROM vcache_entries") as cur:
            row = await cur.fetchone()
            total_entries = row[0] if row else 0

        # Threshold statistics
        thresholds = [at.threshold for at in self._thresholds.values()]
        if thresholds:
            avg_t = sum(thresholds) / len(thresholds)
            min_t = min(thresholds)
            max_t = max(thresholds)
        else:
            avg_t = self._initial_threshold
            min_t = self._initial_threshold
            max_t = self._initial_threshold

        # Threshold distribution: bucket by 0.05 ranges
        distribution: dict[str, int] = {}
        for t in thresholds:
            f"{t:.2f}"[:3] + "0-" + f"{t:.2f}"[:3] + "5"
            # Cleaner bucketing
            lower = int(t * 20) / 20  # round down to nearest 0.05
            upper = lower + 0.05
            bucket_key = f"{lower:.2f}-{upper:.2f}"
            distribution[bucket_key] = distribution.get(bucket_key, 0) + 1

        hit_rate = total_hits / total_queries if total_queries > 0 else 0.0
        fp_rate = total_fp / total_hits if total_hits > 0 else 0.0

        return VCacheStats(
            total_entries=total_entries,
            total_queries=total_queries,
            total_hits=total_hits,
            total_misses=total_misses,
            hit_rate=round(hit_rate, 4),
            eviction_count=self._evictions,
            empirical_fp_rate=round(fp_rate, 6),
            delta_target=self._delta,
            avg_threshold=round(avg_t, 4),
            min_threshold=round(min_t, 4),
            max_threshold=round(max_t, 4),
            threshold_distribution=distribution,
        )

    # ------------------------------------------------------------------
    # Internal stats tracking
    # ------------------------------------------------------------------

    async def _record_hit(self, db: aiosqlite.Connection) -> None:
        await db.execute(
            "UPDATE vcache_global_stats SET total_queries = total_queries + 1, "
            "total_hits = total_hits + 1 WHERE id = 1",
        )
        await db.commit()

    async def _record_miss(self, db: aiosqlite.Connection) -> None:
        await db.execute(
            "UPDATE vcache_global_stats SET total_queries = total_queries + 1, "
            "total_misses = total_misses + 1 WHERE id = 1",
        )
        await db.commit()
