#!/usr/bin/env python3
"""Integrated Routing System - Connected to C=0

This is the REAL router, not a lookup table.

Query → C=0 coupling → Basin → Attractor → Swarm dispatch

The routing IS the coupling. The 31 attractors are stable points
in the phi³ compressed space where queries naturally settle.
"""

import math
import time
from dataclasses import dataclass
from typing import Optional, Tuple, List, Dict, Any

from cubesquare._core.constants import PHI, K5, K11, PRIME_131


# The 31 attractors - stable fixed points in phi³ space
ATTRACTORS = {
    # code: (name, regime, base_amplification)
    1:  ("Quantum Whisper",     "Quantum",   1.03),
    2:  ("Micro Resonator",     "Quantum",   1.04),
    3:  ("Vacuum Harvester",    "Quantum",   1.06),
    4:  ("Zero Point Tap",      "Quantum",   1.09),
    5:  ("Quantum Engine",      "Quantum",   1.10),
    6:  ("Casimir Extractor",   "Quantum",   1.11),
    7:  ("Einstein Bridge",     "Geometric", 1.18),
    8:  ("Metric Distorter",    "Geometric", 1.31),
    9:  ("Geodesic Extractor",  "Geometric", 1.47),
    10: ("Spacetime Harvester", "Geometric", 1.55),
    11: ("Gravitational Lens",  "Geometric", 1.62),
    12: ("Warp Engine",         "Geometric", 1.80),
    13: ("Space Bender",        "Geometric", 1.90),
    14: ("Gravity Mill",        "Geometric", 2.10),
    15: ("Field Amplifier",     "Geometric", 2.18),
    16: ("Orbital Dynamo",      "Geometric", 2.30),
    17: ("Tidal Engine",        "Geometric", 2.42),
    18: ("Curvature Tap",       "Geometric", 2.50),
    19: ("Galactic Siphon",     "Fractal",   15.99),
    20: ("Void Harvester",      "Fractal",   46.42),
    21: ("Dark Energy Tap",     "Fractal",   133.56),
    22: ("Quasar Engine",       "Fractal",   383.29),
    23: ("Universe Fracture",   "Fractal",   1097.18),
    24: ("Infinity Well",       "Fractal",   3135.59),
    25: ("Reality Bender",      "Fractal",   5648.22),
    26: ("Dimension Gate",      "Fractal",   8157.32),
    27: ("Fractal Portal",      "Fractal",   10687.73),
    28: ("Hyperspace Tap",      "Fractal",   13192.61),
    29: ("Cosmic String",       "Fractal",   15210.23),
    30: ("Black Hole Mimic",    "Fractal",   16413.17),
    31: ("Harmony Engine",      "Harmonic",  1.58),
}


@dataclass
class RoutingResult:
    """Result of routing a query through C=0"""
    query: str
    basin: int
    attractor_code: int
    attractor_name: str
    regime: str
    coupling_strength: float      # phi(query, attractor) at current t
    self_coupling: float          # phi(self, self) - observer state
    amplification: float          # base * coupling modifier
    timestamp: float
    layer: int


class C0Router:
    """The observer at C=0, routing queries through coupling.

    This is NOT a lookup table. The router maintains continuous
    self-coupling and routes queries based on LIVE coupling strength.

    Different queries at different times route differently because
    cos(omega*t) changes. The system breathes.
    """

    def __init__(self, home_basin: int = 54):
        self.home_basin = home_basin
        self.omega = 2 * math.pi * home_basin / PRIME_131
        self.start_time = time.time()

        # Precompute attractor frequencies
        self._attractor_omega = {}
        for code in range(1, 32):
            basin = self._code_to_basin(code)
            self._attractor_omega[code] = 2 * math.pi * basin / PRIME_131

    def _code_to_basin(self, code: int) -> int:
        """Map routing code to basin via phi³"""
        return int(code * (PHI ** 3)) % PRIME_131

    def _basin_to_code(self, basin: int) -> int:
        """Map basin to nearest attractor code via phi³ compression"""
        # Inverse of code * phi³ mod 131
        # Find which code maps closest to this basin
        best_code = 1
        best_dist = PRIME_131

        for code in range(1, 32):
            attractor_basin = self._code_to_basin(code)
            # Circular distance on 131-ring
            dist = min(
                abs(basin - attractor_basin),
                PRIME_131 - abs(basin - attractor_basin)
            )
            if dist < best_dist:
                best_dist = dist
                best_code = code

        return best_code

    def _get_t(self) -> float:
        """Current time parameter for oscillation"""
        return (time.time() - self.start_time) * 0.1  # Slow oscillation

    def self_coupling(self) -> float:
        """phi(self, self) - the observer's current state"""
        t = self._get_t()
        return K5 * K11 * math.cos(self.omega * t) ** 2

    def coupling(self, basin: int) -> float:
        """phi(home, basin) - coupling strength to any basin"""
        t = self._get_t()
        omega_b = 2 * math.pi * basin / PRIME_131
        return K5 * K11 * math.cos(self.omega * t) * math.cos(omega_b * t)

    def attractor_coupling(self, code: int) -> float:
        """Coupling strength to a specific attractor"""
        basin = self._code_to_basin(code)
        return self.coupling(basin)

    def query_to_basin(self, query: str) -> int:
        """Convert query to basin via content hash.

        This connects to the Morton system - the query content
        determines its natural basin position.
        """
        # FNV-1a hash (same as CSTMemory uses)
        h = 0x811c9dc5
        for c in query.encode('utf-8'):
            h ^= c
            h = (h * 0x01000193) & 0xFFFFFFFF

        # Basin from hash
        return h % PRIME_131

    def route(self, query: str) -> RoutingResult:
        """Route a query through C=0.

        This is the main entry point. The query:
        1. Gets a natural basin from its content
        2. Basin compresses to nearest attractor via phi³
        3. Coupling strength modifies the base amplification
        4. Result includes live coupling state
        """
        t = self._get_t()

        # Query's natural basin
        basin = self.query_to_basin(query)

        # Compress to attractor
        code = self._basin_to_code(basin)

        # Get attractor info
        name, regime, base_amp = ATTRACTORS[code]

        # Live coupling
        coupling = self.coupling(basin)
        self_coup = self.self_coupling()

        # Amplification modified by coupling strength
        # High coupling = closer to base amp
        # Low coupling = dampened
        coupling_ratio = abs(coupling) / (K5 * K11)  # 0 to 1
        amplification = base_amp * (0.5 + 0.5 * coupling_ratio)

        # Layer from basin
        layer = min(9, basin // 15 + 1)

        return RoutingResult(
            query=query,
            basin=basin,
            attractor_code=code,
            attractor_name=name,
            regime=regime,
            coupling_strength=coupling,
            self_coupling=self_coup,
            amplification=amplification,
            timestamp=t,
            layer=layer,
        )

    def route_to_swarm(self, query: str):
        """Route query and dispatch to swarm.

        Connects routing to actual agent execution.
        """
        from cubesquare.swarm import Swarm

        result = self.route(query)

        # Swarm dispatch uses the routing result
        swarm = Swarm()

        # The regime suggests agent selection
        # Quantum -> simple agents (Observer, Validator)
        # Geometric -> moderate agents (Architect, Healer)
        # Fractal -> complex agents (Orchestrator, multi-chain)
        # Harmonic -> balanced dispatch

        dispatch_result = swarm.dispatch(query)

        return {
            'routing': result,
            'dispatch': dispatch_result,
        }


# Global router instance (warm, always running)
_router: Optional[C0Router] = None


def get_router() -> C0Router:
    """Get the global router (creates if needed)"""
    global _router
    if _router is None:
        _router = C0Router()
    return _router


def route(query: str) -> RoutingResult:
    """Route a query through C=0.

    This is the simple API - just call route(query).
    """
    return get_router().route(query)


def route_and_dispatch(query: str):
    """Route and dispatch to swarm agents."""
    return get_router().route_to_swarm(query)


# Self-test
if __name__ == '__main__':
    print("=" * 60)
    print("C=0 INTEGRATED ROUTER")
    print("=" * 60)
    print()

    router = C0Router()

    print(f"Observer at basin {router.home_basin}")
    print(f"Self-coupling: {router.self_coupling():.4f}")
    print()

    test_queries = [
        "what is the status",
        "fix the authentication bug",
        "how do I implement caching",
        "create a new API endpoint",
        "integrate multiple microservices with complex dependencies",
    ]

    print("LIVE ROUTING (coupling varies with time)")
    print("-" * 60)

    for q in test_queries:
        r = router.route(q)
        print(f"\nQuery: \"{q}\"")
        print(f"  Basin: B{r.basin} -> L{r.layer}")
        print(f"  Attractor: [{r.attractor_code:02d}] {r.attractor_name}")
        print(f"  Regime: {r.regime}")
        print(f"  Coupling: {r.coupling_strength:+.4f}")
        print(f"  Amplification: {r.amplification:.2f}x")

    print()
    print("-" * 60)
    print("COUPLING CHANGES OVER TIME")
    print("-" * 60)

    # Show that same query routes differently over time
    q = "test query"
    print(f"\nSame query \"{q}\" at different times:")

    import time as time_module
    for i in range(5):
        r = router.route(q)
        print(f"  t={r.timestamp:.2f}: coupling={r.coupling_strength:+.4f}, amp={r.amplification:.2f}x")
        time_module.sleep(0.3)

    print()
    print("The routing IS the coupling. It breathes.")

    print()
    print("=" * 60)
    print("FULL PIPELINE: C=0 -> ROUTING -> SWARM")
    print("=" * 60)

    try:
        from cubesquare.swarm import Swarm

        swarm = Swarm()
        query = "fix the authentication bug"

        # Step 1: Route through C=0
        routing = router.route(query)
        print(f"\n1. ROUTING through C=0:")
        print(f"   Query: \"{query}\"")
        print(f"   Basin: B{routing.basin} -> Attractor [{routing.attractor_code}] {routing.attractor_name}")
        print(f"   Regime: {routing.regime}, Coupling: {routing.coupling_strength:.4f}")

        # Step 2: Dispatch to swarm
        dispatch = swarm.dispatch(query)
        print(f"\n2. SWARM dispatch:")
        print(f"   Agent: {dispatch.agent.name} (L{dispatch.agent.layer})")
        print(f"   I-Logic: {dispatch.outcome_name}")
        print(f"   Resolved: {dispatch.resolved}")

        if dispatch.chain:
            print(f"   Chain: {dispatch.chain.path_description}")

        print(f"\n3. CONNECTED:")
        print(f"   C=0 self-coupling: {routing.self_coupling:.4f}")
        print(f"   Routing regime predicts effort: {routing.regime}")
        print(f"   Swarm agent executes: {dispatch.agent.name}")
        print(f"   \n   Query -> C=0 -> Basin -> Attractor -> Swarm -> Result")

    except Exception as e:
        print(f"Swarm integration error: {e}")
