"""cortex/storage/queries/peer_queries.py — Peer modeling storage mixin."""

from __future__ import annotations

from datetime import datetime, timezone
from typing import Any, Dict, List, Optional

from cortex.storage._helpers import _now
from cortex.types import Peer


class PeerQueryMixin:
    """Mixin providing peerquery methods."""

    def _row_to_peer(row) -> "Peer":
        """Convert a DB row (aiosqlite.Row) to a Peer dataclass."""
        return Peer(
            id=row["id"],
            owner_id=row["owner_id"],
            entity_id=row["entity_id"],
            display_name=row["display_name"],
            relationship=row["relationship"] or "unknown",
            first_interaction=row["first_interaction"],
            last_interaction=row["last_interaction"],
            interaction_count=row["interaction_count"],
            representation=row["representation"] or "{}",
            created_at=row["created_at"],
            updated_at=row["updated_at"],
        )


    async def create_peer(
        self,
        owner_id: str,
        display_name: str,
        entity_id: Optional[str] = None,
        relationship: str = "unknown",
        first_interaction: Optional[str] = None,
        last_interaction: Optional[str] = None,
        interaction_count: int = 1,
        representation: str = "{}",
        created_at: Optional[str] = None,
        updated_at: Optional[str] = None,
    ) -> Peer:
        """Insert a new peer row and return the created Peer."""
        async with self._write_lock:
            conn = await self._get_conn()
            now = _now()
            first_interaction = first_interaction or now
            last_interaction = last_interaction or now
            created_at = created_at or now
            updated_at = updated_at or now

            async with conn.execute(
                """
                INSERT INTO peer (
                    owner_id, entity_id, display_name, relationship,
                    first_interaction, last_interaction, interaction_count,
                    representation, created_at, updated_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                RETURNING *
                """,
                (
                    owner_id, entity_id, display_name, relationship,
                    first_interaction, last_interaction, interaction_count,
                    representation, created_at, updated_at,
                ),
            ) as cur:
                row = await cur.fetchone()
            await conn.commit()
            return self._row_to_peer(row)


    async def get_peer(self, peer_id: str) -> Optional[Peer]:
        """Fetch a peer by primary key. Returns None if not found."""
        conn = await self._get_conn()
        async with conn.execute(
            "SELECT * FROM peer WHERE id = ?", (peer_id,)
        ) as cur:
            row = await cur.fetchone()
        return self._row_to_peer(row) if row else None


    async def get_peer_by_entity(self, entity_id: str) -> Optional[Peer]:
        """Fetch the first peer linked to the given entity_id."""
        conn = await self._get_conn()
        async with conn.execute(
            "SELECT * FROM peer WHERE entity_id = ? LIMIT 1", (entity_id,)
        ) as cur:
            row = await cur.fetchone()
        return self._row_to_peer(row) if row else None


    async def list_peers(self, owner_id: str = "default") -> List[Peer]:
        """Return all peers for an owner, ordered by last_interaction DESC."""
        conn = await self._get_conn()
        async with conn.execute(
            """
            SELECT * FROM peer
            WHERE owner_id = ?
            ORDER BY last_interaction DESC
            """,
            (owner_id,),
        ) as cur:
            rows = await cur.fetchall()
        return [self._row_to_peer(r) for r in rows]


    async def update_peer(self, peer_id: str, **kwargs) -> Peer:
        """Update arbitrary fields on a peer. updated_at is auto-set if not supplied."""
        allowed = {
            "entity_id", "display_name", "relationship",
            "first_interaction", "last_interaction", "interaction_count",
            "representation", "owner_id", "updated_at",
        }
        fields = {k: v for k, v in kwargs.items() if k in allowed}
        if not fields:
            existing = await self.get_peer(peer_id)
            if existing is None:
                raise ValueError(f"Peer not found: {peer_id}")
            return existing
        fields.setdefault("updated_at", _now())

        set_clause = ", ".join(f"{col} = ?" for col in fields)
        values = list(fields.values()) + [peer_id]
        async with self._write_lock:
            conn = await self._get_conn()
            await conn.execute(
                f"UPDATE peer SET {set_clause} WHERE id = ?", values
            )
            await conn.commit()
            updated = await self.get_peer(peer_id)
            if updated is None:
                raise ValueError(f"Peer not found after update: {peer_id}")
            return updated


    async def delete_peer(self, peer_id: str) -> None:
        """Delete a peer by primary key."""
        async with self._write_lock:
            conn = await self._get_conn()
            await conn.execute("DELETE FROM peer WHERE id = ?", (peer_id,))
            await conn.commit()

    async def get_active_peer_ids(
        self,
        owner_id: str,
        since_dt: datetime,
        limit: int = 20,
    ) -> List[str]:
        """Return peer IDs that have claims referencing them since *since_dt*.

        A peer is considered active if any claim's subject_entity_id or
        object_entity_id matches the peer's entity_id, and the claim was
        created after *since_dt*.

        Args:
            owner_id: Scoping owner identifier.
            since_dt: Only consider claims created after this datetime.
            limit: Maximum number of peer IDs to return.

        Returns:
            List of peer id strings (most recently active first).
        """
        conn = await self._get_conn()
        since_iso = since_dt.isoformat() if since_dt.tzinfo else \
            since_dt.replace(tzinfo=timezone.utc).isoformat()
        async with conn.execute(
            """
            SELECT DISTINCT p.id
            FROM peer p
            INNER JOIN semantic_claim sc
                ON (sc.subject_entity_id = p.entity_id
                    OR sc.object_entity_id = p.entity_id)
            WHERE p.owner_id = ?
              AND p.entity_id IS NOT NULL
              AND sc.created_at >= ?
              AND sc.status = 'active'
            ORDER BY MAX(sc.created_at) DESC
            LIMIT ?
            """,
            (owner_id, since_iso, limit),
        ) as cur:
            rows = await cur.fetchall()
        return [row["id"] for row in rows]


