"""L4 FUNCTIONAL Layer

Filter by abstraction level.
"""
from __future__ import annotations

from typing import List, Optional

from .base import Layer, LayerID, Token


class L4_Functional(Layer):
    """L4 FUNCTIONAL: Filter by abstraction level."""

    LAYER_ID = LayerID.FUNCTIONAL
    LAYER_NAME = "FUNCTIONAL"
    LAYER_DESCRIPTION = "Filter by abstraction level"

    # Abstraction levels mapped to diagonal ranges
    LEVELS = {
        "architecture": (0, 50),      # High-level design
        "module": (51, 150),          # Module/package level
        "class": (151, 300),          # Class/type definitions
        "function": (301, 500),       # Function/method level
        "implementation": (501, 700), # Implementation details
        "line": (701, 999),           # Line-level code
    }

    def query(self, pattern: str, limit: int = 100) -> List[Token]:
        """Query by abstraction level."""
        level = pattern.lower()
        if level in self.LEVELS:
            min_diag, max_diag = self.LEVELS[level]
            return [t for t in self._memory._tokens.values()
                    if min_diag <= t.diagonal <= max_diag][:limit]
        return []

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

        if "level" in criteria:
            level = criteria["level"].lower()
            if level in self.LEVELS:
                min_diag, max_diag = self.LEVELS[level]
                results = [t for t in results if min_diag <= t.diagonal <= max_diag]

        if "min_diagonal" in criteria:
            min_d = criteria["min_diagonal"]
            results = [t for t in results if t.diagonal >= min_d]

        if "max_diagonal" in criteria:
            max_d = criteria["max_diagonal"]
            results = [t for t in results if t.diagonal <= max_d]

        return results

    def navigate(self, token: Token, direction: str) -> Optional[Token]:
        """Navigate in functional direction."""
        if direction == "abstract":
            # Find more abstract (lower diagonal)
            for t in self._memory._tokens.values():
                if t.basin == token.basin and t.diagonal < token.diagonal:
                    return t
        elif direction == "concrete":
            # Find more concrete (higher diagonal)
            for t in self._memory._tokens.values():
                if t.basin == token.basin and t.diagonal > token.diagonal:
                    return t
        return None

    def distance(self, a: Token, b: Token) -> float:
        """Functional distance: diagonal difference."""
        return abs(a.diagonal - b.diagonal)

    def abstraction_level(self, token: Token) -> str:
        """Get abstraction level name for token."""
        for level, (min_d, max_d) in self.LEVELS.items():
            if min_d <= token.diagonal <= max_d:
                return level
        return "unknown"
