"""Delta sync engine for efficient cross-device replication."""

from __future__ import annotations

import logging
from typing import Any

from neurovault.network.bundle import KnowledgeBundle
from neurovault.network.strategies import NewerWinsStrategy, SyncStrategy
from neurovault.network.sync_log import SyncLog

logger = logging.getLogger(__name__)


class DeltaSyncEngine:
    """Bidirectional sync engine using append-only delta log replay."""

    def __init__(
        self,
        storage,
        bridge,
        sync_log: SyncLog,
        strategy: SyncStrategy | None = None,
    ):
        self._storage = storage
        self._bridge = bridge
        self._log = sync_log
        self._strategy = strategy or NewerWinsStrategy()
        self._peer_states: dict[str, int] = {}

    def create_delta(self, peer_id: str) -> KnowledgeBundle:
        """Create delta bundle since last known sequence for peer."""
        last_seq = int(self._peer_states.get(peer_id, 0))
        entries = self._log.get_since(last_seq)

        memories: list[dict[str, Any]] = []
        entities: list[dict[str, Any]] = []
        relationships: list[dict[str, Any]] = []

        seen_memories: set[str] = set()
        seen_entities: set[str] = set()

        for entry in entries:
            op = str(entry.get("op", ""))
            data = entry.get("data", {}) if isinstance(entry.get("data"), dict) else {}

            if op == "memory_added":
                memory_id = data.get("memory_id") or data.get("id")
                if not memory_id:
                    continue
                mem = self._storage.get_memory(memory_id)
                if not mem:
                    continue
                mid = str(mem.get("id", ""))
                if mid in seen_memories:
                    continue
                seen_memories.add(mid)
                memories.append(mem)

            elif op == "entity_upserted":
                entity_name = str(data.get("name", ""))
                if not entity_name:
                    continue
                ent = self._storage.get_entity_by_name(entity_name)
                if ent is None:
                    continue
                eid = ent.id.hex() if ent.id else ""
                if eid in seen_entities:
                    continue
                seen_entities.add(eid)
                entities.append(
                    {
                        "id": eid,
                        "name": ent.name,
                        "entity_type": ent.entity_type,
                        "importance": ent.importance,
                        "mention_count": ent.mention_count,
                        "first_seen": ent.first_seen,
                        "last_seen": ent.last_seen,
                        "metadata": ent.metadata,
                    }
                )

            elif op == "relationship_added":
                relationships.append(data)

        return KnowledgeBundle(
            source_device_id=getattr(self._bridge, "get_active_persona_name", lambda: "")(),
            memories=memories,
            entities=entities,
            relationships=relationships,
            checkpoint_id=str(self._log.get_latest_seq()),
        )

    def apply_delta(self, bundle: KnowledgeBundle, peer_id: str) -> dict[str, int]:
        """Apply incoming delta and update local peer checkpoint state."""
        stats = {"memories": 0, "entities": 0, "relationships": 0, "conflicts": 0}

        for remote_mem in bundle.memories:
            local = self._storage.find_memory_by_hash(remote_mem.get("content_hash", ""))
            if local is None:
                self._storage.insert_remote_memory(remote_mem)
                stats["memories"] += 1
                continue

            resolved = self._strategy.resolve_memory_conflict(local, remote_mem)
            if resolved.get("_conflict"):
                stats["conflicts"] += 1
                continue

            self._storage.update_memory_from_remote(resolved)
            stats["memories"] += 1

        for remote_ent in bundle.entities:
            local = self._storage.find_entity_by_name(str(remote_ent.get("name", "")))
            if local is None:
                self._storage.insert_remote_entity(remote_ent)
                stats["entities"] += 1
                continue

            local_dict = {
                "name": local.name,
                "entity_type": local.entity_type,
                "importance": local.importance,
                "mention_count": local.mention_count,
                "last_seen": local.last_seen,
            }
            resolved = self._strategy.resolve_entity_conflict(local_dict, remote_ent)
            if resolved.get("_conflict"):
                stats["conflicts"] += 1
                continue
            self._storage.update_entity_from_remote(resolved)
            stats["entities"] += 1

        for rel in bundle.relationships:
            self._storage.upsert_remote_relationship(rel)
            stats["relationships"] += 1

        checkpoint = str(bundle.checkpoint_id or "")
        if checkpoint.isdigit():
            self._peer_states[peer_id] = int(checkpoint)

        return stats

    def get_sync_status(self) -> dict[str, Any]:
        return {
            "local_seq": self._log.get_latest_seq(),
            "peer_states": dict(self._peer_states),
            "log_integrity": self._log.verify_integrity(),
        }
