"""L2 SEMANTIC Layer

Query by meaning and concept using Fernandez basins.
"""
from __future__ import annotations

from typing import List, Dict, Optional
from collections import defaultdict

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


class L2_Semantic(Layer):
    """L2 SEMANTIC: Query by meaning and concept."""

    LAYER_ID = LayerID.SEMANTIC
    LAYER_NAME = "SEMANTIC"
    LAYER_DESCRIPTION = "Query by meaning and concept"

    def query(self, pattern: str, limit: int = 100) -> List[Token]:
        """Query by semantic meaning."""
        pattern_bytes = pattern.lower().encode()
        target_basin = sum(pattern_bytes) % PRIME_131

        scored = []
        for token in self._memory._tokens.values():
            basin_match = 1.0 if token.basin == target_basin else 0.0

            keyword_score = 0.0
            if token.path:
                path_lower = token.path.lower()
                if pattern.lower() in path_lower:
                    keyword_score = 0.5
                for word in pattern.lower().split():
                    if word in path_lower:
                        keyword_score += 0.1

            basin_distance = min(
                abs(token.basin - target_basin),
                PRIME_131 - abs(token.basin - target_basin)
            )
            proximity_score = 1.0 - (basin_distance / (PRIME_131 / 2))

            total_score = basin_match + keyword_score + proximity_score * 0.3
            if total_score > 0.3:
                scored.append((token, total_score))

        scored.sort(key=lambda x: -x[1])
        return [t for t, _ in scored[:limit]]

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

        if "basin" in criteria:
            target = criteria["basin"]
            results = [t for t in results if t.basin == target]

        if "basin_range" in criteria:
            min_basin, max_basin = criteria["basin_range"]
            results = [t for t in results if min_basin <= t.basin <= max_basin]

        if "concept" in criteria:
            concept = criteria["concept"]
            concept_basin = sum(concept.lower().encode()) % PRIME_131
            results = [t for t in results if abs(t.basin - concept_basin) <= 10]

        return results

    def navigate(self, token: Token, direction: str) -> Optional[Token]:
        """Navigate in semantic direction."""
        if direction == "similar":
            related = self.related(token, limit=2)
            return related[0] if related else None

        elif direction == "opposite":
            opposite_basin = (token.basin + PRIME_131 // 2) % PRIME_131
            for t in self._memory._tokens.values():
                if t.basin == opposite_basin:
                    return t
            return None

        elif direction == "broader":
            for t in self._memory._tokens.values():
                if t.basin == token.basin and t.diagonal < token.diagonal:
                    return t
            return None

        elif direction == "narrower":
            for t in self._memory._tokens.values():
                if t.basin == token.basin and t.diagonal > token.diagonal:
                    return t
            return None

        return None

    def distance(self, a: Token, b: Token) -> float:
        """Semantic distance: basin distance with wrapping."""
        direct = abs(a.basin - b.basin)
        wrapped = PRIME_131 - direct
        return min(direct, wrapped)

    def cluster_id(self, token: Token) -> int:
        """Get semantic cluster (basin) for token."""
        return token.basin

    def cluster_tokens(self, basin: int) -> List[Token]:
        """Get all tokens in a semantic cluster."""
        return [t for t in self._memory._tokens.values() if t.basin == basin]

    def basin_stats(self) -> Dict[int, int]:
        """Get token count per basin."""
        stats = defaultdict(int)
        for token in self._memory._tokens.values():
            stats[token.basin] += 1
        return dict(stats)
