"""L7 FREQUENCY Layer

Attention and energy mapping.
"""
from __future__ import annotations

from typing import List, Optional
import math

from .base import Layer, LayerID, Token
from .._core import K5


class L7_Frequency(Layer):
    """L7 FREQUENCY: Attention and energy mapping."""

    LAYER_ID = LayerID.FREQUENCY
    LAYER_NAME = "FREQUENCY"
    LAYER_DESCRIPTION = "Attention and energy mapping"

    # Bubble frequency at centroid: K5 * 36 = 41.72 Hz
    BUBBLE_FREQUENCY = K5 * 36

    def query(self, pattern: str, limit: int = 100) -> List[Token]:
        """Query by frequency pattern."""
        pattern = pattern.lower()

        if pattern == "high_energy":
            return sorted(self._memory._tokens.values(),
                         key=lambda t: t.energy, reverse=True)[:limit]
        elif pattern == "low_energy":
            return sorted(self._memory._tokens.values(),
                         key=lambda t: t.energy)[:limit]
        elif pattern == "resonant":
            # Tokens near bubble frequency
            target_freq = self.BUBBLE_FREQUENCY
            scored = []
            for t in self._memory._tokens.values():
                freq = self._compute_frequency(t)
                diff = abs(freq - target_freq)
                scored.append((t, diff))
            scored.sort(key=lambda x: x[1])
            return [t for t, _ in scored[:limit]]

        return list(self._memory._tokens.values())[:limit]

    def _compute_frequency(self, token: Token) -> float:
        """Compute frequency for token from energy and diagonal."""
        return (token.energy / 100) * (token.diagonal / 10) + 20

    def filter(self, tokens: List[Token], **criteria) -> List[Token]:
        """Filter by frequency criteria."""
        results = tokens

        if "min_frequency" in criteria:
            min_f = criteria["min_frequency"]
            results = [t for t in results if self._compute_frequency(t) >= min_f]

        if "max_frequency" in criteria:
            max_f = criteria["max_frequency"]
            results = [t for t in results if self._compute_frequency(t) <= max_f]

        return results

    def navigate(self, token: Token, direction: str) -> Optional[Token]:
        """Navigate by frequency."""
        my_freq = self._compute_frequency(token)

        if direction == "higher":
            candidates = [t for t in self._memory._tokens.values()
                         if self._compute_frequency(t) > my_freq]
            return min(candidates, key=lambda t: self._compute_frequency(t)) if candidates else None
        elif direction == "lower":
            candidates = [t for t in self._memory._tokens.values()
                         if self._compute_frequency(t) < my_freq]
            return max(candidates, key=lambda t: self._compute_frequency(t)) if candidates else None
        return None

    def distance(self, a: Token, b: Token) -> float:
        """Frequency distance."""
        freq_a = self._compute_frequency(a)
        freq_b = self._compute_frequency(b)
        return abs(freq_a - freq_b)

    def frequency(self, token: Token) -> float:
        """Get frequency for token."""
        return self._compute_frequency(token)

    def wavelength(self, token: Token) -> float:
        """Get wavelength for token (inverse of frequency)."""
        freq = self._compute_frequency(token)
        return 1.0 / freq if freq > 0 else float('inf')
