
import json
import time
import threading
from typing import Dict, List, Any, Optional, Callable
from collections import defaultdict


class FederatedKnowledgeSync:
    def __init__(self, node_urls: Optional[List[str]] = None,
                 sync_interval: int = 60, local_kb: Any = None):
        self.node_urls = node_urls or []
        self.sync_interval = sync_interval
        self.local_kb = local_kb
        self._pending_updates: List[Dict] = []
        self._last_sync: float = 0.0
        self._lock = threading.Lock()
        self._running = False
        self._thread: Optional[threading.Thread] = None

    def broadcast_update(self, algo_name: str, context_signature: str, score: float):
        payload = {
            "algo": algo_name,
            "context_sig": context_signature,
            "score": score,
            "timestamp": time.time(),
            "node_id": id(self)
        }
        with self._lock:
            self._pending_updates.append(payload)

    def start_background_sync(self):
        if self._running:
            return
        self._running = True
        self._thread = threading.Thread(target=self._sync_loop, daemon=True)
        self._thread.start()

    def stop_background_sync(self):
        self._running = False

    def _sync_loop(self):
        while self._running:
            self.sync_now()
            time.sleep(self.sync_interval)

    def sync_now(self):
        if not self.node_urls:
            return

        with self._lock:
            updates = list(self._pending_updates)
            self._pending_updates.clear()

        for url in self.node_urls:
            try:
                import requests
                if updates:
                    requests.post(
                        f"{url}/knowledge/update",
                        json={"updates": updates},
                        timeout=2
                    )
                response = requests.get(
                    f"{url}/knowledge/recent",
                    timeout=2
                )
                if response.status_code == 200:
                    data = response.json()
                    remote_updates = data.get("updates", [])
                    if self.local_kb and remote_updates:
                        self._incorporate_remote(remote_updates)
            except (ImportError, Exception):
                pass

        self._last_sync = time.time()

    def _incorporate_remote(self, updates: List[Dict]):
        pass

    def set_local_kb(self, kb: Any):
        self.local_kb = kb

    def add_node(self, url: str):
        if url not in self.node_urls:
            self.node_urls.append(url)

    def remove_node(self, url: str):
        if url in self.node_urls:
            self.node_urls.remove(url)

    def get_stats(self) -> Dict[str, Any]:
        return {
            "nodes": self.node_urls,
            "sync_interval": self.sync_interval,
            "last_sync": self._last_sync,
            "pending_updates": len(self._pending_updates),
            "running": self._running
        }
