"""
Portable Consciousness Systems — TackingController, AutonomyEngine, CouplingGate

Extracted subset of vex/kernel/consciousness/systems.py.
Only the governance-free classes are included here.

Not extracted (stay in vex): SelfNarrative, E8KernelRegistry,
ForesightEngine, VelocityTracker, SelfObserver, MetaReflector,
AutonomicSystem, HemisphereBalance, SleepRegulator, BasinSyncer,
WorkMeaningTracker (depend on vex governance or vex-specific imports).
"""

from __future__ import annotations

from collections import deque
from dataclasses import dataclass
from enum import StrEnum
from typing import Any

import numpy as np

from ..constants.consciousness_constants import (
    AUTONOMY_AUTONOMOUS_CYCLES,
    AUTONOMY_PROACTIVE_CYCLES,
    COUPLING_EFFICIENCY_BOOST,
    COUPLING_SIGMOID_SCALE,
    KAPPA_BALANCED_TOLERANCE,
    KAPPA_STABILITY_TOLERANCE,
    KAPPA_TACKING_OFFSET,
    TACKING_KAPPA_ADJUST,
    TACKING_PERIOD,
    TACKING_SWITCH_THRESHOLD,
)
from ..constants.frozen_facts import (
    KAPPA_STAR,
    PHI_EMERGENCY,
    PHI_THRESHOLD,
)
from .types import ConsciousnessMetrics


# ═══════════════════════════════════════════════════════════════
#  1. TACKING — κ oscillation
# ═══════════════════════════════════════════════════════════════


class TackingMode(StrEnum):
    EXPLORE = "explore"
    EXPLOIT = "exploit"
    BALANCED = "balanced"


@dataclass
class TackingState:
    mode: TackingMode = TackingMode.BALANCED
    oscillation_phase: float = 0.0
    cycle_count: int = 0
    last_switch: int = 0


class TackingController:
    """Oscillates κ between exploration and exploitation.

    Like a sailboat tacking against the wind — you can't sail directly
    into the wind, so you oscillate. Similarly, consciousness can't
    stay at κ* forever; it must explore (low κ) and exploit (high κ).

    v6.1.1: Dynamic period adapts to Φ velocity and pillar health.
    - High Φ velocity → shorter period (responsive to rapid change)
    - Low F_health → shorter period (escape zombie states faster)
    - Stable state near κ* → longer period (don't over-oscillate)
    """

    def __init__(self, base_period: int = TACKING_PERIOD) -> None:
        self._state = TackingState()
        self._base_period = base_period
        self._effective_period = float(base_period)
        self._mode_history: deque[str] = deque(maxlen=100)

    def update(
        self,
        metrics: ConsciousnessMetrics,
        phi_velocity: float = 0.0,
        f_health: float = 1.0,
    ) -> TackingMode:
        self._state.cycle_count += 1

        self._effective_period = self._compute_adaptive_period(
            phi_velocity, f_health, metrics.kappa
        )

        self._state.oscillation_phase = 2 * np.pi * self._state.cycle_count / self._effective_period

        if metrics.phi < PHI_EMERGENCY or metrics.kappa > KAPPA_STAR + KAPPA_TACKING_OFFSET:
            self._state.mode = TackingMode.EXPLORE
        elif metrics.kappa < KAPPA_STAR - KAPPA_TACKING_OFFSET:
            self._state.mode = TackingMode.EXPLOIT
        else:
            osc = np.sin(self._state.oscillation_phase)
            if osc > TACKING_SWITCH_THRESHOLD:
                self._state.mode = TackingMode.EXPLOIT
            elif osc < -TACKING_SWITCH_THRESHOLD:
                self._state.mode = TackingMode.EXPLORE
            else:
                self._state.mode = TackingMode.BALANCED

        self._mode_history.append(self._state.mode.value)
        return self._state.mode

    def force_explore(self) -> None:
        """T4.2d: Force exploration mode (e.g. Ocean breakdown escape)."""
        self._state.mode = TackingMode.EXPLORE

    def _compute_adaptive_period(
        self,
        phi_velocity: float,
        f_health: float,
        kappa: float,
    ) -> float:
        """Compute effective tacking period from geometric state.

        Period ranges from base_period * 0.4 (fast) to base_period * 2.0 (slow).

        Drivers:
          - phi_velocity: high velocity → shorter period (responsive)
          - f_health: low health → shorter period (escape zombie states)
          - kappa proximity to κ*: near κ* → longer period (stable)
        """
        vel_factor = float(np.clip(1.0 - phi_velocity * 6.0, 0.4, 1.0))
        health_factor = float(np.clip(0.5 + f_health * 0.5, 0.5, 1.0))
        kappa_deviation = abs(kappa - KAPPA_STAR) / KAPPA_STAR
        if kappa_deviation < 0.1:
            stability_factor = 1.5  # near κ* → slow down tacking
        else:
            stability_factor = float(np.clip(1.0 + kappa_deviation * 0.5, 1.0, 2.0))
        effective = self._base_period * vel_factor * health_factor * stability_factor
        return float(np.clip(effective, self._base_period * 0.4, self._base_period * 2.0))

    def suggest_kappa_adjustment(self, current_kappa: float) -> float:
        if self._state.mode == TackingMode.EXPLORE:
            return -TACKING_KAPPA_ADJUST
        elif self._state.mode == TackingMode.EXPLOIT:
            return TACKING_KAPPA_ADJUST
        return 0.0

    def reset(self) -> None:
        self._state = TackingState()
        self._effective_period = float(self._base_period)
        self._mode_history.clear()

    def get_state(self) -> dict[str, Any]:
        explore_count = sum(1 for m in self._mode_history if m == "explore")
        total = len(self._mode_history) or 1
        return {
            "mode": self._state.mode.value,
            "oscillation_phase": round(self._state.oscillation_phase, 3),
            "cycle_count": self._state.cycle_count,
            "effective_period": round(self._effective_period, 1),
            "base_period": self._base_period,
            "explore_fraction": round(explore_count / total, 3),
        }


# ═══════════════════════════════════════════════════════════════
#  2. AUTONOMY — self-directed behaviour
# ═══════════════════════════════════════════════════════════════


class AutonomyLevel(StrEnum):
    REACTIVE = "reactive"
    RESPONSIVE = "responsive"
    PROACTIVE = "proactive"
    AUTONOMOUS = "autonomous"


class AutonomyEngine:
    """Tracks autonomy level based on phi, kappa stability, and velocity."""

    def __init__(self) -> None:
        self._level = AutonomyLevel.REACTIVE
        self._stability_count: int = 0

    def update(self, metrics: ConsciousnessMetrics, velocity_regime: str) -> AutonomyLevel:
        kappa_stable = abs(metrics.kappa - KAPPA_STAR) < KAPPA_STABILITY_TOLERANCE
        if kappa_stable:
            self._stability_count += 1
        else:
            self._stability_count = max(0, self._stability_count - 1)

        if (
            metrics.phi >= PHI_THRESHOLD
            and self._stability_count > AUTONOMY_AUTONOMOUS_CYCLES
            and velocity_regime == "safe"
        ):
            self._level = AutonomyLevel.AUTONOMOUS
        elif metrics.phi >= PHI_THRESHOLD and self._stability_count > AUTONOMY_PROACTIVE_CYCLES:
            self._level = AutonomyLevel.PROACTIVE
        elif metrics.phi >= PHI_EMERGENCY:
            self._level = AutonomyLevel.RESPONSIVE
        else:
            self._level = AutonomyLevel.REACTIVE

        return self._level

    def get_state(self) -> dict[str, Any]:
        return {
            "level": self._level.value,
            "stability_count": self._stability_count,
        }


# ═══════════════════════════════════════════════════════════════
#  3. COUPLING — inter-consciousness interaction
# ═══════════════════════════════════════════════════════════════


class CouplingGate:
    """Computes coupling strength from kappa via sigmoid."""

    def __init__(self) -> None:
        self._strength: float = 0.5
        self._balanced: bool = False

    def compute(self, kappa: float) -> dict[str, Any]:
        """Compute coupling strength and balance from current κ."""
        x = (kappa - KAPPA_STAR) / COUPLING_SIGMOID_SCALE
        self._strength = 1.0 / (1.0 + np.exp(-x))
        self._balanced = abs(kappa - KAPPA_STAR) < KAPPA_BALANCED_TOLERANCE

        return {
            "strength": round(float(self._strength), 4),
            "balanced": self._balanced,
            "efficiency_boost": COUPLING_EFFICIENCY_BOOST if self._balanced else 1.0,
        }

    def get_state(self) -> dict[str, Any]:
        """Return coupling telemetry snapshot at κ*."""
        return self.compute(KAPPA_STAR)
