# echo_cog/fibpi3d_scheduler.py
"""
Minimal stand-in for the real FibPi3D wave scheduler.

- Maintains a monotonically increasing wave counter per task
- Uses a golden-angle increment on [0,1) as a cheap low-discrepancy sequence
- Maps phase into a small number of 'ports' (e.g., 8) for routing

This is enough to:
  * log wave/port in receipts
  * condition prompts on wave/port
  * later swap in the real FibPi3D lattice scheduler with the same API
"""

from __future__ import annotations

import math
from dataclasses import dataclass
from typing import Dict


# Golden ratio conjugate: (sqrt(5) - 1) / 2 ≈ 0.618...
_GOLDEN_RATIO_CONJUGATE = (math.sqrt(5.0) - 1.0) / 2.0


@dataclass
class WaveInfo:
    """Information about the current wave for a given task."""
    wave_id: int
    phase: float      # [0, 1) golden-angle derived
    port: int         # Discretized routing port


class FibPi3DScheduler:
    """
    Minimal FibPi3D wave scheduler stub.

    Uses a golden-angle (Kronecker) sequence to generate low-discrepancy
    phase values, which are then mapped to discrete 'ports' for routing.

    This provides:
      - Deterministic wave IDs per task
      - Golden-angle phase for diversity
      - Port routing for potential specialist steering
    """

    def __init__(self, num_ports: int = 8) -> None:
        self.num_ports = max(1, num_ports)
        self._counters: Dict[str, int] = {}

    def next_wave(self, task: str) -> WaveInfo:
        """Get the next wave for a task."""
        n = self._counters.get(task, 0)
        self._counters[task] = n + 1

        # Simple Kronecker sequence on [0,1)
        phase = (n * _GOLDEN_RATIO_CONJUGATE) % 1.0
        port = int(phase * self.num_ports)

        return WaveInfo(wave_id=n, phase=phase, port=port)

    def reset_task(self, task: str) -> None:
        """Reset wave counter for a specific task."""
        self._counters[task] = 0

    def reset_all(self) -> None:
        """Reset all wave counters."""
        self._counters.clear()

    def get_wave_count(self, task: str) -> int:
        """Get the current wave count for a task."""
        return self._counters.get(task, 0)
