"""
aca/ai_engine/services/intent_layerer.py

Intent Layering
───────────────
Goes beyond keyword matching to understand the *purpose* behind a query.
A user asking for "a winter coat for hiking" should get outdoor/technical
results weighted higher, even if they never typed "outdoor" or "technical".

Integration point:
    Call `enrich_query(slots, raw_text)` inside ConversationManager after
    initial slot extraction, before building the product query.

Example:
    slots = {"category": "coat", "season": "winter"}
    enriched = enrich_query(slots, "I need a winter coat for hiking")
    # enriched["purpose"] == "hiking"
    # enriched["_intent_tags"] == ["outdoor", "technical", "waterproof"]
    # enriched["_boosted_attributes"] == ["waterproof", "insulated", "durable"]
"""

import re
import logging
from typing import Any, Dict, List, Optional, Set

logger = logging.getLogger(__name__)


# ─────────────────────────────────────────────────────────────────────────────
# Intent pattern definitions
# ─────────────────────────────────────────────────────────────────────────────

# Each entry: (regex pattern, intent_tags, boosted_attributes, inferred_slots)
# Matched against the raw user message (case-insensitive).
INTENT_PATTERNS = [
    # ── Outdoor / activity ──────────────────────────────────────────
    (
        r"\b(hiking|trekking|trail|backpacking|camping)\b",
        ["outdoor", "technical"],
        ["waterproof", "durable", "lightweight", "insulated"],
        {"purpose": "hiking", "use_case": "outdoor"},
    ),
    (
        r"\b(running|jogging|marathon|5k)\b",
        ["sport", "running"],
        ["breathable", "lightweight", "moisture-wicking"],
        {"purpose": "running", "use_case": "sport"},
    ),
    (
        r"\b(gym|workout|training|weightlifting|crossfit)\b",
        ["sport", "gym"],
        ["stretch", "durable", "moisture-wicking"],
        {"purpose": "gym", "use_case": "sport"},
    ),
    (
        r"\b(swimming|surf|beach|water\s?sport)\b",
        ["water", "outdoor"],
        ["waterproof", "quick-dry", "UV protection"],
        {"purpose": "swimming", "use_case": "outdoor"},
    ),
    (
        r"\b(cycling|biking|mountain\s?bike|mtb)\b",
        ["sport", "cycling"],
        ["padded", "breathable", "reflective", "aerodynamic"],
        {"purpose": "cycling", "use_case": "sport"},
    ),
    # ── Occasion / formality ─────────────────────────────────────────
    (
        r"\b(wedding|formal|black\s?tie|gala|ceremony)\b",
        ["formal", "occasion"],
        ["elegant", "tailored", "premium"],
        {"formality": "formal", "occasion": "wedding"},
    ),
    (
        r"\b(office|work|professional|business|interview)\b",
        ["professional", "office"],
        ["structured", "tailored", "classic"],
        {"formality": "smart", "use_case": "office"},
    ),
    (
        r"\b(casual|everyday|weekend|relaxed|lounge)\b",
        ["casual"],
        ["comfortable", "relaxed fit"],
        {"formality": "casual"},
    ),
    (
        r"\b(party|nightout|night\s?out|club|cocktail)\b",
        ["social", "occasion"],
        ["stylish", "trendy"],
        {"occasion": "party", "formality": "smart-casual"},
    ),
    # ── Weather / season context ─────────────────────────────────────
    (
        r"\b(winter|cold|freezing|snow|icy)\b",
        ["cold-weather"],
        ["insulated", "warm", "thermal", "fleece"],
        {"season": "winter"},
    ),
    (
        r"\b(summer|hot|heat|warm\s?weather|sunny)\b",
        ["warm-weather"],
        ["lightweight", "breathable", "linen", "UV protection"],
        {"season": "summer"},
    ),
    (
        r"\b(rain|wet|waterproof|drizzle|monsoon)\b",
        ["wet-weather"],
        ["waterproof", "water-resistant", "windproof"],
        {},
    ),
    # ── Intent / need ────────────────────────────────────────────────
    (
        r"\b(gift|present|for\s+(my|a|his|her|their))\b",
        ["gift"],
        [],
        {"intent": "gift"},
    ),
    (
        r"\b(cheap|budget|affordable|save\s+money|low\s+price)\b",
        ["price-sensitive"],
        ["value", "affordable"],
        {"price_priority": "budget"},
    ),
    (
        r"\b(premium|luxury|high.?end|quality|invest)\b",
        ["quality-sensitive"],
        ["premium", "high-quality", "durable"],
        {"price_priority": "premium"},
    ),
    (
        r"\b(kids?|children|toddler|baby|infant|school)\b",
        ["children"],
        ["durable", "safe", "washable"],
        {"audience": "children"},
    ),
]

# Attribute weight boosts: how much to increase an attribute's ranking weight
# when an intent tag is active (additive, 0.0–1.0 scale)
INTENT_WEIGHT_BOOSTS = {
    "outdoor": {"durable": 0.25, "waterproof": 0.20, "lightweight": 0.15},
    "technical": {"waterproof": 0.20, "durable": 0.20, "insulated": 0.15},
    "cold-weather": {"insulated": 0.30, "warm": 0.25},
    "warm-weather": {"lightweight": 0.30, "breathable": 0.25},
    "wet-weather": {"waterproof": 0.35, "water-resistant": 0.30},
    "sport": {"breathable": 0.20, "lightweight": 0.20, "durable": 0.15},
    "formal": {"tailored": 0.30, "elegant": 0.25},
    "professional": {"tailored": 0.25, "structured": 0.20},
    "price-sensitive": {"price": 0.40},
    "quality-sensitive": {"brand": 0.20, "durable": 0.20},
    "gift": {"premium": 0.10},
    "children": {"durable": 0.25, "safe": 0.30},
}


# ─────────────────────────────────────────────────────────────────────────────
# Public API
# ─────────────────────────────────────────────────────────────────────────────

def enrich_query(slots: dict, raw_text: str) -> dict:
    """
    Takes extracted slots and the raw user message, then:
      1. Detects intent patterns in the text
      2. Fills any missing slots the intent implies (e.g. season, formality)
      3. Adds _intent_tags and _boosted_attributes to the slots dict
      4. Adds _attribute_weight_boosts for the ranking layer to consume

    Returns an enriched copy of `slots` — does not mutate the original.
    """
    enriched = dict(slots)
    intent_tags: Set[str] = set()
    boosted_attributes: List[str] = []
    weight_boosts: Dict[str, float] = {}

    text_lower = raw_text.lower()

    for pattern, tags, attrs, inferred_slots in INTENT_PATTERNS:
        if re.search(pattern, text_lower):
            intent_tags.update(tags)
            boosted_attributes.extend(attrs)

            # Only fill inferred slots if the slot is not already set
            for key, value in inferred_slots.items():
                if key not in enriched or not enriched[key]:
                    enriched[key] = value
                    logger.debug("Intent layerer inferred slot %s=%s", key, value)

    # Compute attribute weight boosts from all matched intent tags
    for tag in intent_tags:
        for attr, boost in INTENT_WEIGHT_BOOSTS.get(tag, {}).items():
            weight_boosts[attr] = min(1.0, weight_boosts.get(attr, 0.0) + boost)

    enriched["_intent_tags"] = sorted(intent_tags)
    enriched["_boosted_attributes"] = list(dict.fromkeys(boosted_attributes))  # deduped
    enriched["_attribute_weight_boosts"] = weight_boosts

    if intent_tags:
        logger.info("Intent layerer matched tags: %s", sorted(intent_tags))

    return enriched


def build_prompt_context(enriched_slots: dict) -> str:
    """
    Returns a short natural-language string describing the inferred context.
    Injected into the system/context portion of the AI prompt so the LLM
    understands the user's purpose even without re-reading the whole history.

    Example output:
        "User context: outdoor hiking use in winter weather. Prioritise:
         waterproof, insulated, durable."
    """
    tags = enriched_slots.get("_intent_tags", [])
    attrs = enriched_slots.get("_boosted_attributes", [])

    if not tags and not attrs:
        return ""

    parts = []
    if tags:
        parts.append(f"inferred context: {', '.join(tags)}")
    if attrs:
        top_attrs = attrs[:5]
        parts.append(f"prioritise attributes: {', '.join(top_attrs)}")

    return "User " + "; ".join(parts) + "."