"""L5 PROBABILISTIC Layer

Match by usage patterns.
"""
from __future__ import annotations

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

from .base import Layer, LayerID, Token


class L5_Probabilistic(Layer):
    """L5 PROBABILISTIC: Match by usage patterns."""

    LAYER_ID = LayerID.PROBABILISTIC
    LAYER_NAME = "PROBABILISTIC"
    LAYER_DESCRIPTION = "Match by usage patterns"

    def __init__(self, memory):
        super().__init__(memory)
        self._access_counts: Dict[int, int] = defaultdict(int)
        self._access_times: Dict[int, float] = {}

    def _record_access(self, token: Token):
        """Record access to token for pattern tracking."""
        self._access_counts[token.morton] += 1
        self._access_times[token.morton] = time.time()

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

        if pattern == "frequent":
            # Most accessed tokens
            sorted_by_count = sorted(
                self._memory._tokens.values(),
                key=lambda t: self._access_counts.get(t.morton, 0),
                reverse=True
            )
            return sorted_by_count[:limit]

        elif pattern == "recent":
            # Most recently accessed
            sorted_by_time = sorted(
                self._memory._tokens.values(),
                key=lambda t: self._access_times.get(t.morton, 0),
                reverse=True
            )
            return sorted_by_time[:limit]

        elif pattern == "rare":
            # Least accessed
            sorted_by_count = sorted(
                self._memory._tokens.values(),
                key=lambda t: self._access_counts.get(t.morton, 0)
            )
            return sorted_by_count[:limit]

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

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

        if "min_access" in criteria:
            min_a = criteria["min_access"]
            results = [t for t in results if self._access_counts.get(t.morton, 0) >= min_a]

        if "max_access" in criteria:
            max_a = criteria["max_access"]
            results = [t for t in results if self._access_counts.get(t.morton, 0) <= max_a]

        if "accessed_after" in criteria:
            after = criteria["accessed_after"]
            results = [t for t in results if self._access_times.get(t.morton, 0) > after]

        return results

    def navigate(self, token: Token, direction: str) -> Optional[Token]:
        """Navigate by usage pattern."""
        if direction == "more_frequent":
            my_count = self._access_counts.get(token.morton, 0)
            candidates = [t for t in self._memory._tokens.values()
                         if self._access_counts.get(t.morton, 0) > my_count]
            if candidates:
                return min(candidates, key=lambda t: self._access_counts.get(t.morton, 0))
        elif direction == "less_frequent":
            my_count = self._access_counts.get(token.morton, 0)
            candidates = [t for t in self._memory._tokens.values()
                         if self._access_counts.get(t.morton, 0) < my_count]
            if candidates:
                return max(candidates, key=lambda t: self._access_counts.get(t.morton, 0))
        return None

    def distance(self, a: Token, b: Token) -> float:
        """Probabilistic distance: difference in access patterns."""
        count_a = self._access_counts.get(a.morton, 0)
        count_b = self._access_counts.get(b.morton, 0)
        max_count = max(count_a, count_b, 1)
        return abs(count_a - count_b) / max_count

    def access_count(self, token: Token) -> int:
        """Get access count for token."""
        return self._access_counts.get(token.morton, 0)

    def last_access(self, token: Token) -> Optional[float]:
        """Get last access time for token."""
        return self._access_times.get(token.morton)
