"""Claim delete/restore/purge and health-check mixin."""
from __future__ import annotations

from typing import Optional

from cortex.storage._helpers import _now, _row_to_claim
from cortex.types import SemanticClaim


class ClaimLifecycleMixin:
    """Mixin providing claim deletion, restoration, purging, and ping."""

    async def delete_claim(
        self, claim_id: str, owner_id: str, hard: bool = False
    ) -> None:
        """Delete a semantic claim.

        - ``hard=False`` (default): soft-delete — sets ``is_deleted=1`` and
          ``deleted_at=NOW()``.  The row remains in the DB and can be restored
          within the 90-day retention window.
        - ``hard=True``: permanently removes the row from the database.
        """
        conn = await self._get_conn()
        async with self._write_lock:
            if hard:
                await conn.execute(
                    "DELETE FROM semantic_claim WHERE id = ? AND owner_id = ?",
                    (claim_id, owner_id),
                )
            else:
                now = _now()
                await conn.execute(
                    """
                    UPDATE semantic_claim
                    SET is_deleted = 1, deleted_at = ?, status = 'archived', updated_at = ?
                    WHERE id = ? AND owner_id = ?
                    """,
                    (now, now, claim_id, owner_id),
                )
            # Sync FTS: remove entry — soft-deleted claims should not appear in
            # search results.
            await conn.execute(
                "DELETE FROM fts_claims WHERE claim_id = ?",
                (claim_id,),
            )
            # Sync vec_index: remove embedding so retrieval doesn't find
            # ghost entries (#1395).
            await conn.execute(
                "DELETE FROM embedding_index WHERE item_type = 'claim' AND item_id = ?",
                (claim_id,),
            )
            if self._vec_loaded:
                try:
                    await conn.execute(
                        "DELETE FROM vec_index WHERE item_type = 'claim' AND item_id = ?",
                        (claim_id,),
                    )
                except Exception:
                    pass  # vec_index may not exist; non-fatal
            await conn.commit()

    async def restore_claim(self, claim_id: str, owner_id: str) -> Optional[SemanticClaim]:
        """Restore a soft-deleted claim within the 90-day retention window.

        Clears ``is_deleted`` and ``deleted_at``, sets status back to 'active',
        and re-inserts the FTS index entry.

        Returns the restored SemanticClaim, or None if the claim was not found
        (either it doesn't exist or was already hard-deleted / purged).
        """
        conn = await self._get_conn()
        claim = await self.get_claim(claim_id, owner_id=owner_id, include_deleted=True)
        if claim is None:
            return None

        async with self._write_lock:
            now = _now()
            await conn.execute(
                """
                UPDATE semantic_claim
                SET is_deleted = 0, deleted_at = NULL, status = 'active', updated_at = ?
                WHERE id = ? AND owner_id = ?
                """,
                (now, claim_id, owner_id),
            )
            # Re-insert FTS entry
            fts_content = f"{claim.subject} {claim.predicate} {claim.object_value}"
            await conn.execute(
                "INSERT OR REPLACE INTO fts_claims(claim_id, content) VALUES (?, ?)",
                (claim_id, fts_content),
            )
            await conn.commit()

        return await self.get_claim(claim_id, owner_id=owner_id)

    async def purge_deleted_claims(self, retention_days: int = 90) -> int:
        """Permanently remove soft-deleted claims older than ``retention_days``.

        Called by the dream cycle to enforce the 90-day retention window.
        Returns the number of rows permanently deleted.
        """
        conn = await self._get_conn()
        cutoff = f"-{retention_days} days"
        async with self._write_lock:
            # Find claims to purge
            async with conn.execute(
                """
                SELECT id FROM semantic_claim
                WHERE is_deleted = 1
                  AND deleted_at < datetime('now', ?)
                """,
                (cutoff,),
            ) as cur:
                rows = await cur.fetchall()

            purged_ids = [r["id"] for r in rows]
            if not purged_ids:
                return 0

            placeholders = ",".join("?" for _ in purged_ids)

            # Remove FTS entries
            await conn.execute(
                f"DELETE FROM fts_claims WHERE claim_id IN ({placeholders})",
                purged_ids,
            )
            # Remove embeddings
            await conn.execute(
                f"DELETE FROM embedding_index WHERE item_type = 'claim' AND item_id IN ({placeholders})",
                purged_ids,
            )
            # Remove from vec_index if loaded
            if self._vec_loaded:
                try:
                    await conn.execute(
                        f"DELETE FROM vec_index WHERE item_type = 'claim' AND item_id IN ({placeholders})",
                        purged_ids,
                    )
                except Exception:
                    pass
            # Remove provenance
            await conn.execute(
                f"DELETE FROM claim_provenance WHERE claim_id IN ({placeholders})",
                purged_ids,
            )
            # Finally, hard-delete the claims
            await conn.execute(
                f"DELETE FROM semantic_claim WHERE id IN ({placeholders})",
                purged_ids,
            )
            await conn.commit()

        import logging
        logging.getLogger(__name__).info(
            "purge_deleted_claims: permanently removed %d claims older than %d days",
            len(purged_ids),
            retention_days,
        )
        return len(purged_ids)

    async def ping(self) -> bool:
        """Verify DB connection with a lightweight SELECT 1."""
        import logging
        _logger = logging.getLogger(__name__)
        try:
            conn = await self._get_conn()
            async with conn.execute("SELECT 1") as cur:
                await cur.fetchone()
            return True
        except Exception as e:
            _logger.warning("ping: DB connection check failed: %s", e)
            return False
