"""
Language-agnostic review router for cost-optimized LLM classification.

Routes reviews to different processing paths based on structural signals only:
- SKIP: Extremely low-value reviews (skip LLM entirely, assign generic code)
- CHEAP_MODEL: Short, simple reviews (use Haiku for classification)
- FULL_MODEL: Complex reviews (use Sonnet for full classification)

IMPORTANT: All routing decisions use ONLY language-agnostic signals:
- Word count / character count (numeric)
- Presence of numbers in text (pattern-based)
- Sentence count (punctuation-based)
- Emoji-only detection (pattern-based)
- Star rating (numeric)

NO hardcoded word lists (like "great", "bueno", "gut") are used because:
- Reviews span 7+ languages (Spanish, English, Dutch, German, Polish, Finnish, Danish, etc.)
- Typography errors are common
- False negatives (skipping valuable reviews) are worse than false positives
"""

from __future__ import annotations

import logging
import re
from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from reviewiq_pipeline.contracts import ReviewToClassify

logger = logging.getLogger(__name__)


class RoutingTier(Enum):
    """Processing tier for a review."""

    SKIP = "skip"           # Skip LLM, assign generic URT code
    CHEAP_MODEL = "cheap"   # Use fast/cheap model (Haiku)
    FULL_MODEL = "full"     # Use full model (Sonnet)


@dataclass
class RoutingDecision:
    """Result of routing decision for a review."""

    tier: RoutingTier
    reason: str
    signals: dict[str, any]
    # For SKIP tier, pre-assign the generic classification
    skip_classification: dict | None = None


@dataclass
class RouterConfig:
    """Configuration for the review router."""

    # SKIP tier thresholds (very conservative - prefer false positives)
    skip_max_words: int = 1
    skip_max_chars: int = 15
    skip_require_extreme_rating: bool = True  # Only skip if rating is 1 or 5

    # CHEAP_MODEL tier thresholds
    cheap_max_words: int = 10
    cheap_max_chars: int = 100

    # Signals that force FULL_MODEL regardless of length
    full_model_if_has_numbers: bool = True
    full_model_if_multiple_sentences: bool = True
    full_model_min_sentences: int = 2


class ReviewRouter:
    """
    Routes reviews to appropriate processing tier using language-agnostic signals.

    Design principles:
    - Conservative: Prefer false positives (processing simple reviews fully)
      over false negatives (skipping valuable reviews)
    - Language-agnostic: No word lists, only structural/numeric signals
    - Transparent: Every decision includes the signals used
    """

    # Pattern to detect numbers (dates, amounts, room numbers, etc.)
    NUMBER_PATTERN = re.compile(r'\d+')

    # Pattern for sentence-ending punctuation (language-agnostic)
    SENTENCE_END_PATTERN = re.compile(r'[.!?。！？]+')

    # Emoji pattern (same as TextProcessor)
    EMOJI_PATTERN = re.compile(
        "["
        "\U0001F600-\U0001F64F"  # emoticons
        "\U0001F300-\U0001F5FF"  # symbols & pictographs
        "\U0001F680-\U0001F6FF"  # transport & map symbols
        "\U0001F1E0-\U0001F1FF"  # flags
        "\U00002702-\U000027B0"  # dingbats
        "\U000024C2-\U0001F251"  # enclosed characters
        "]+",
        flags=re.UNICODE,
    )

    # Generic classification for skipped reviews
    GENERIC_POSITIVE = {
        "urt_primary": "V4.03",  # Overall Satisfaction - General
        "valence": "V+",
        "intensity": "I1",
        "confidence": "low",
        "skip_reason": "auto_routed_positive",
    }

    GENERIC_NEGATIVE = {
        "urt_primary": "V4.03",  # Overall Satisfaction - General
        "valence": "V-",
        "intensity": "I1",
        "confidence": "low",
        "skip_reason": "auto_routed_negative",
    }

    def __init__(self, config: RouterConfig | None = None):
        self.config = config or RouterConfig()
        self._stats = {
            "skip": 0,
            "cheap": 0,
            "full": 0,
        }

    def route(self, review: ReviewToClassify) -> RoutingDecision:
        """
        Determine the processing tier for a review.

        Args:
            review: Review to route

        Returns:
            RoutingDecision with tier, reason, and signals
        """
        text = review.get("text_normalized") or review.get("text") or ""
        rating = review.get("rating", 3)

        # Extract language-agnostic signals
        signals = self._extract_signals(text, rating)

        # Decision logic (conservative - start with FULL, demote only if safe)
        decision = self._make_decision(signals, rating)

        # Update stats
        self._stats[decision.tier.value] += 1

        return decision

    def route_batch(
        self,
        reviews: list[ReviewToClassify]
    ) -> dict[RoutingTier, list[ReviewToClassify]]:
        """
        Route a batch of reviews, grouping by tier.

        Args:
            reviews: List of reviews to route

        Returns:
            Dictionary mapping tiers to lists of reviews
        """
        result = {
            RoutingTier.SKIP: [],
            RoutingTier.CHEAP_MODEL: [],
            RoutingTier.FULL_MODEL: [],
        }

        for review in reviews:
            decision = self.route(review)
            # Attach routing decision to review for downstream use
            review["_routing"] = decision
            result[decision.tier].append(review)

        logger.info(
            f"Routed {len(reviews)} reviews: "
            f"SKIP={len(result[RoutingTier.SKIP])}, "
            f"CHEAP={len(result[RoutingTier.CHEAP_MODEL])}, "
            f"FULL={len(result[RoutingTier.FULL_MODEL])}"
        )

        return result

    def _extract_signals(self, text: str, rating: int) -> dict[str, any]:
        """
        Extract language-agnostic signals from review text.

        All signals are structural/numeric, never word-based.
        """
        if not text:
            return {
                "word_count": 0,
                "char_count": 0,
                "has_numbers": False,
                "sentence_count": 0,
                "emoji_count": 0,
                "is_emoji_only": False,
                "rating": rating,
                "is_extreme_rating": rating in (1, 5),
            }

        words = text.split()
        word_count = len(words)
        char_count = len(text)

        # Check for numbers (dates, amounts, room numbers - often signal specific details)
        has_numbers = bool(self.NUMBER_PATTERN.search(text))

        # Count sentences by punctuation
        sentences = self.SENTENCE_END_PATTERN.split(text)
        sentence_count = len([s for s in sentences if s.strip()])

        # Count emoji
        emoji_matches = self.EMOJI_PATTERN.findall(text)
        emoji_count = len(emoji_matches)

        # Check if text is emoji-only (after stripping whitespace)
        text_without_emoji = self.EMOJI_PATTERN.sub("", text).strip()
        is_emoji_only = emoji_count > 0 and len(text_without_emoji) == 0

        return {
            "word_count": word_count,
            "char_count": char_count,
            "has_numbers": has_numbers,
            "sentence_count": sentence_count,
            "emoji_count": emoji_count,
            "is_emoji_only": is_emoji_only,
            "rating": rating,
            "is_extreme_rating": rating in (1, 5),
        }

    def _make_decision(
        self,
        signals: dict[str, any],
        rating: int
    ) -> RoutingDecision:
        """
        Make routing decision based on signals.

        Decision order (conservative):
        1. Check for FULL_MODEL forcing signals first
        2. Check for SKIP eligibility (very strict)
        3. Check for CHEAP_MODEL eligibility
        4. Default to FULL_MODEL
        """
        cfg = self.config

        # FULL_MODEL forcing conditions
        if cfg.full_model_if_has_numbers and signals["has_numbers"]:
            return RoutingDecision(
                tier=RoutingTier.FULL_MODEL,
                reason="contains_numbers",
                signals=signals,
            )

        if (cfg.full_model_if_multiple_sentences and
            signals["sentence_count"] >= cfg.full_model_min_sentences):
            return RoutingDecision(
                tier=RoutingTier.FULL_MODEL,
                reason="multiple_sentences",
                signals=signals,
            )

        if signals["word_count"] > cfg.cheap_max_words:
            return RoutingDecision(
                tier=RoutingTier.FULL_MODEL,
                reason="long_text",
                signals=signals,
            )

        # SKIP eligibility (very strict)
        skip_eligible = (
            signals["word_count"] <= cfg.skip_max_words and
            signals["char_count"] <= cfg.skip_max_chars and
            not signals["has_numbers"] and
            signals["sentence_count"] <= 1
        )

        if cfg.skip_require_extreme_rating:
            skip_eligible = skip_eligible and signals["is_extreme_rating"]

        if skip_eligible:
            # Determine generic classification based on rating
            if rating >= 4:
                skip_class = self.GENERIC_POSITIVE.copy()
            else:
                skip_class = self.GENERIC_NEGATIVE.copy()

            return RoutingDecision(
                tier=RoutingTier.SKIP,
                reason="trivial_review",
                signals=signals,
                skip_classification=skip_class,
            )

        # CHEAP_MODEL eligibility
        if (signals["word_count"] <= cfg.cheap_max_words and
            signals["char_count"] <= cfg.cheap_max_chars and
            signals["sentence_count"] <= 1):
            return RoutingDecision(
                tier=RoutingTier.CHEAP_MODEL,
                reason="short_simple_review",
                signals=signals,
            )

        # Default to FULL_MODEL
        return RoutingDecision(
            tier=RoutingTier.FULL_MODEL,
            reason="default",
            signals=signals,
        )

    def get_stats(self) -> dict[str, int]:
        """Get routing statistics."""
        return self._stats.copy()

    def reset_stats(self):
        """Reset routing statistics."""
        self._stats = {"skip": 0, "cheap": 0, "full": 0}


def create_router(
    conservative: bool = True,
    skip_enabled: bool = True,
    cheap_model_enabled: bool = True,
) -> ReviewRouter:
    """
    Factory function to create a router with common configurations.

    Args:
        conservative: If True, use very strict thresholds (recommended)
        skip_enabled: If True, allow SKIP tier
        cheap_model_enabled: If True, allow CHEAP_MODEL tier

    Returns:
        Configured ReviewRouter instance
    """
    if conservative:
        # Very conservative - only skip 1-word reviews with extreme ratings
        config = RouterConfig(
            skip_max_words=1,
            skip_max_chars=15,
            skip_require_extreme_rating=True,
            cheap_max_words=10,
            cheap_max_chars=100,
            full_model_if_has_numbers=True,
            full_model_if_multiple_sentences=True,
            full_model_min_sentences=2,
        )
    else:
        # Less conservative - skip more, cheaper processing
        config = RouterConfig(
            skip_max_words=3,
            skip_max_chars=30,
            skip_require_extreme_rating=False,
            cheap_max_words=15,
            cheap_max_chars=150,
            full_model_if_has_numbers=True,
            full_model_if_multiple_sentences=True,
            full_model_min_sentences=3,
        )

    # Override if tiers disabled
    if not skip_enabled:
        config.skip_max_words = 0
        config.skip_max_chars = 0

    if not cheap_model_enabled:
        config.cheap_max_words = 0
        config.cheap_max_chars = 0

    return ReviewRouter(config)
