import asyncio
import json
import logging
import random
from datetime import datetime
from uuid import uuid4
from pydantic import BaseModel

from soagila.db.event_bus import publish_event

logger = logging.getLogger("soagila.cifce.simulator")
CHANNEL = "soagila:metrics"


class Institution(BaseModel):
    id: str
    name: str
    location: str
    city: str
    lat: float
    lng: float
    sector: str
    status: str = "connected"
    memories_shared: int = 0
    patterns_contributed: int = 0


class FederationEvent(BaseModel):
    id: str
    source_institution: str
    target_institutions: list[str]
    event_type: str  # pattern_shared / fraud_alert / insight_propagated / compliance_sync
    description: str
    latency_ms: float
    encrypted: bool = True
    timestamp: datetime


class FederationStatus(BaseModel):
    running: bool = False
    connected_institutions: int = 5
    total_insights: int = 0
    patterns_shared: int = 0
    privacy_violations: int = 0  # Always 0
    avg_propagation_ms: float = 0.0
    events: list[FederationEvent] = []


# 5 institutions
INSTITUTIONS = [
    Institution(id="istanbul", name="Anatolian Digital Bank", location="Turkey", city="Istanbul", lat=41.0082, lng=28.9784, sector="banking"),
    Institution(id="frankfurt", name="European Savings Group", location="Germany", city="Frankfurt", lat=50.1109, lng=8.6821, sector="banking"),
    Institution(id="singapore", name="Pacific Wealth Holdings", location="Singapore", city="Singapore", lat=1.3521, lng=103.8198, sector="wealth_management"),
    Institution(id="sao_paulo", name="LatAm Fintech", location="Brazil", city="São Paulo", lat=-23.5505, lng=-46.6333, sector="fintech"),
    Institution(id="london", name="Royal Health Trust", location="UK", city="London", lat=51.5074, lng=-0.1278, sector="healthcare"),
]

# Event templates
EVENT_TEMPLATES = [
    {"type": "fraud_alert", "templates": [
        "Fraud pattern detected in {source_city}: unusual transaction cluster propagated to {count} institutions",
        "Cross-border fraud signature identified by {source_name}, alert shared with {target_names}",
        "Emerging payment fraud pattern from {source_city} region distributed to federated network",
    ]},
    {"type": "insight_propagated", "templates": [
        "Customer behavior insight from {source_name}: {insight} — shared with {count} peers",
        "New semantic pattern from {source_city}: fee sensitivity increasing — propagated globally",
        "Medical interaction insight shared across {count} institutions via encrypted channel",
    ]},
    {"type": "pattern_shared", "templates": [
        "Semantic memory pattern promoted at {source_name}, shared with federated network",
        "Cross-institutional pattern: {insight} — learned from {source_city} data",
        "Behavioral pattern from {source_name} distributed to {count} partner institutions",
    ]},
    {"type": "compliance_sync", "templates": [
        "Compliance policy update from {source_city}: new regulation parameters synchronized",
        "GDPR compliance evidence shared by {source_name} with EU partners",
        "Cross-jurisdictional audit data synchronized between {source_city} and {target_names}",
    ]},
]

INSIGHTS = [
    "customers prefer digital-first onboarding",
    "loan inquiry patterns shifting to mobile",
    "high-net-worth segment showing interest in ESG",
    "credit card dispute patterns converging globally",
    "retirement planning queries increasing 23%",
    "cross-border payment friction points identified",
    "insurance bundling shows 15% higher retention",
]


class FederationSimulator:
    def __init__(self):
        self.status = FederationStatus()
        self._task: asyncio.Task | None = None
        self._stop_event = asyncio.Event()

    async def start(self, interval_seconds: float = 8.0):
        if self.status.running:
            return
        self.status.running = True
        self._stop_event.clear()
        self._task = asyncio.create_task(self._run(interval_seconds))
        logger.info("Federation simulator started")

    async def stop(self):
        if not self.status.running:
            return
        self._stop_event.set()
        if self._task:
            self._task.cancel()
            try:
                await self._task
            except asyncio.CancelledError:
                pass
        self.status.running = False
        logger.info("Federation simulator stopped")

    def get_status(self) -> FederationStatus:
        return self.status

    def get_institutions(self) -> list[Institution]:
        return INSTITUTIONS

    async def _run(self, interval: float):
        try:
            while not self._stop_event.is_set():
                event = self._generate_event()
                self.status.events.insert(0, event)
                self.status.events = self.status.events[:30]  # Keep last 30
                self.status.total_insights += 1
                if event.event_type == "pattern_shared":
                    self.status.patterns_shared += 1

                # Update avg latency
                latencies = [e.latency_ms for e in self.status.events]
                self.status.avg_propagation_ms = sum(latencies) / len(latencies)

                # Update institution metrics
                for inst in INSTITUTIONS:
                    if inst.id == event.source_institution:
                        inst.patterns_contributed += 1
                    if inst.id in event.target_institutions:
                        inst.memories_shared += 1

                # Publish via Redis
                await publish_event(CHANNEL, json.dumps({
                    "type": "federation_event",
                    "event_id": event.id,
                    "event_type": event.event_type,
                    "source": event.source_institution,
                    "targets": event.target_institutions,
                    "description": event.description,
                    "latency_ms": event.latency_ms,
                    "total_insights": self.status.total_insights,
                    "patterns_shared": self.status.patterns_shared,
                    "privacy_violations": 0,
                    "timestamp": event.timestamp.isoformat(),
                }))

                try:
                    await asyncio.wait_for(self._stop_event.wait(), timeout=interval)
                    break
                except asyncio.TimeoutError:
                    pass
        except asyncio.CancelledError:
            pass

    def _generate_event(self) -> FederationEvent:
        source = random.choice(INSTITUTIONS)
        targets = random.sample([i for i in INSTITUTIONS if i.id != source.id], k=random.randint(1, 3))
        target_ids = [t.id for t in targets]
        target_names = ", ".join([t.name for t in targets])

        category = random.choice(EVENT_TEMPLATES)
        template = random.choice(category["templates"])

        description = template.format(
            source_city=source.city,
            source_name=source.name,
            target_names=target_names,
            count=len(targets),
            insight=random.choice(INSIGHTS),
        )

        # Simulate realistic latency based on distance
        latency = random.uniform(120, 4500)

        return FederationEvent(
            id=str(uuid4()),
            source_institution=source.id,
            target_institutions=target_ids,
            event_type=category["type"],
            description=description,
            latency_ms=round(latency, 1),
            timestamp=datetime.utcnow(),
        )


# Singleton
_simulator: FederationSimulator | None = None

def get_federation_simulator() -> FederationSimulator:
    global _simulator
    if _simulator is None:
        _simulator = FederationSimulator()
    return _simulator
