"""
Config Resolver - Resolves L1 config + sector brief for classification.

Builds a single JSON payload per business containing:
- Enabled primitives (L1 + always-on meta)
- Weights
- Sector brief (language/signals)
- Minimal primitive dictionary
"""

from __future__ import annotations

import json
import logging
from pathlib import Path
from typing import Any, TYPE_CHECKING

if TYPE_CHECKING:
    import asyncpg

logger = logging.getLogger(__name__)

# Paths
DATA_DIR = Path(__file__).parent.parent.parent.parent / "data"
CONFIGS_DIR = DATA_DIR / "primitive_configs" / "l1"
BRIEFS_DIR = DATA_DIR / "sector_briefs"
PRIMITIVES_FILE = DATA_DIR / "primitives.json"

# Meta primitives - always enabled, never weighted
META_PRIMITIVES = frozenset([
    "HONESTY", "ETHICS", "PROMISES",  # Trust
    "ACKNOWLEDGMENT", "RESPONSE_QUALITY", "RECOVERY",  # Resolution
    "RETURN_INTENT", "RECOMMEND", "RECOGNITION",  # Loyalty
    "UNMAPPED",  # Escape
])

# Core primitives dictionary (frozen 36 - minimal for prompt)
CORE_PRIMITIVES = {
    # Quality (8)
    "TASTE": {"domain": "O", "name": "Taste/Flavor", "def": "Sensory quality of food/beverage"},
    "CRAFT": {"domain": "O", "name": "Craftsmanship", "def": "Skill of execution/preparation"},
    "FRESHNESS": {"domain": "O", "name": "Freshness", "def": "Newness, not stale or old"},
    "TEMPERATURE": {"domain": "O", "name": "Temperature", "def": "Hot/cold as expected"},
    "EFFECTIVENESS": {"domain": "O", "name": "Effectiveness", "def": "Achieves intended purpose"},
    "ACCURACY": {"domain": "O", "name": "Accuracy", "def": "Correct, as ordered/specified"},
    "CONDITION": {"domain": "O", "name": "Condition", "def": "Physical state, wear, damage"},
    "CONSISTENCY": {"domain": "O", "name": "Consistency", "def": "Same quality each time"},
    # Service (4)
    "MANNER": {"domain": "P", "name": "Manner/Attitude", "def": "Friendliness, respect, warmth"},
    "COMPETENCE": {"domain": "P", "name": "Competence", "def": "Knowledge and skill of staff"},
    "ATTENTIVENESS": {"domain": "P", "name": "Attentiveness", "def": "Being present, responsive"},
    "COMMUNICATION": {"domain": "P", "name": "Communication", "def": "Clarity, listening, updates"},
    # Process (4)
    "SPEED": {"domain": "J", "name": "Speed/Wait", "def": "Time to service, waiting"},
    "FRICTION": {"domain": "J", "name": "Friction", "def": "Obstacles, hassles, complexity"},
    "RELIABILITY": {"domain": "J", "name": "Reliability", "def": "Dependable, keeps promises"},
    "AVAILABILITY": {"domain": "J", "name": "Availability", "def": "Open when needed, bookable"},
    # Environment (6)
    "CLEANLINESS": {"domain": "E", "name": "Cleanliness", "def": "Hygiene, tidiness"},
    "COMFORT": {"domain": "E", "name": "Comfort", "def": "Physical ease, seating"},
    "SAFETY": {"domain": "E", "name": "Safety", "def": "Free from harm/danger"},
    "AMBIANCE": {"domain": "E", "name": "Ambiance", "def": "Atmosphere, mood, vibe"},
    "ACCESSIBILITY": {"domain": "E", "name": "Accessibility", "def": "Easy to reach, navigate"},
    "DIGITAL_UX": {"domain": "E", "name": "Digital Experience", "def": "Website, app, online"},
    # Value (4)
    "PRICE_LEVEL": {"domain": "V", "name": "Price Level", "def": "Absolute cost (cheap/expensive)"},
    "PRICE_FAIRNESS": {"domain": "V", "name": "Price Fairness", "def": "Reasonable for what you get"},
    "PRICE_TRANSPARENCY": {"domain": "V", "name": "Price Transparency", "def": "No hidden fees, clear pricing"},
    "VALUE_FOR_MONEY": {"domain": "V", "name": "Value for Money", "def": "Worth what you paid"},
}


class ConfigResolver:
    """
    Resolves classification config for a business.

    Usage:
        resolver = ConfigResolver()
        payload = await resolver.resolve("Go Karts Mar Menor", pool)
    """

    def __init__(self):
        self._l1_cache: dict[str, dict] = {}
        self._brief_cache: dict[str, dict] = {}

    def _load_l1_config(self, sector_code: str) -> dict[str, Any] | None:
        """Load L1 config from file."""
        if sector_code in self._l1_cache:
            return self._l1_cache[sector_code]

        config_path = CONFIGS_DIR / f"{sector_code.lower()}_config.json"
        if not config_path.exists():
            logger.warning(f"No L1 config for sector {sector_code}")
            return None

        with open(config_path) as f:
            config = json.load(f)

        self._l1_cache[sector_code] = config
        return config

    def _load_sector_brief(self, sector_code: str) -> dict[str, Any] | None:
        """Load sector brief from file."""
        if sector_code in self._brief_cache:
            return self._brief_cache[sector_code]

        brief_path = BRIEFS_DIR / f"{sector_code.lower()}_brief.json"
        if not brief_path.exists():
            logger.warning(f"No sector brief for {sector_code}")
            return None

        with open(brief_path) as f:
            brief = json.load(f)

        self._brief_cache[sector_code] = brief
        return brief

    async def get_business_mapping(
        self,
        pool: asyncpg.Pool,
        business_id: str,
    ) -> dict[str, Any] | None:
        """Get business → taxonomy mapping from database."""
        query = """
            SELECT business_id, gbp_path::text, sector_code
            FROM pipeline.business_taxonomy_map
            WHERE business_id = $1
        """
        row = await pool.fetchrow(query, business_id)
        if not row:
            return None
        return dict(row)

    def resolve_enabled_set(self, l1_config: dict) -> set[str]:
        """
        Compute final enabled primitive set.

        = L1.enabled + META_PRIMITIVES (always-on)
        """
        enabled = set(l1_config.get("enabled", []))
        enabled.update(META_PRIMITIVES)
        return enabled

    def resolve_weights(self, l1_config: dict) -> dict[str, float]:
        """Get weights from L1 config."""
        return dict(l1_config.get("weights", {}))

    def build_primitives_for_prompt(
        self,
        enabled: set[str],
        weights: dict[str, float],
    ) -> dict[str, dict]:
        """
        Build minimal primitives dict for prompt.

        Only includes enabled primitives with their definitions.
        """
        result = {}
        for prim in enabled:
            if prim in CORE_PRIMITIVES:
                entry = CORE_PRIMITIVES[prim].copy()
                if prim in weights:
                    entry["weight"] = weights[prim]
                result[prim] = entry
            elif prim in META_PRIMITIVES:
                # Meta primitives - minimal entry
                result[prim] = {"domain": "M", "name": prim.replace("_", " ").title(), "meta": True}
        return result

    def extract_brief_signals(self, brief: dict) -> dict[str, Any]:
        """
        Extract relevant signals from sector brief for prompt.

        Keeps it minimal to avoid bloating context.
        """
        if not brief:
            return {}

        return {
            "sector": brief.get("sector_code"),
            "what_customers_judge": brief.get("what_customers_judge"),
            "critical_pain_points": brief.get("critical_pain_points"),
            "industry_terminology": brief.get("industry_terminology"),
        }

    async def resolve(
        self,
        business_id: str,
        pool: asyncpg.Pool,
        mode: str | None = None,
    ) -> dict[str, Any] | None:
        """
        Resolve full classification payload for a business.

        Args:
            business_id: Business identifier
            pool: Database connection pool
            mode: Optional service mode (e.g., "dine_in", "delivery")

        Returns:
            Classification payload or None if business not mapped
        """
        # Get business mapping
        mapping = await self.get_business_mapping(pool, business_id)
        if not mapping:
            logger.warning(f"Business not mapped: {business_id}")
            return None

        sector_code = mapping["sector_code"]
        gbp_path = mapping["gbp_path"]

        # Load L1 config
        l1_config = self._load_l1_config(sector_code)
        if not l1_config:
            logger.warning(f"No L1 config for {sector_code}, using defaults")
            l1_config = {"enabled": list(CORE_PRIMITIVES.keys()), "weights": {}}

        # Load sector brief
        brief = self._load_sector_brief(sector_code)

        # Resolve enabled set and weights
        enabled = self.resolve_enabled_set(l1_config)
        weights = self.resolve_weights(l1_config)

        # Build primitives for prompt
        primitives = self.build_primitives_for_prompt(enabled, weights)

        # Extract brief signals
        brief_signals = self.extract_brief_signals(brief)

        # Build payload
        payload = {
            "business_id": business_id,
            "gbp_path": gbp_path,
            "sector_code": sector_code,
            "config_version": l1_config.get("config_version", "1.0"),
            "modes": [mode] if mode else ["in_person"],
            "default_mode": mode or "in_person",
            "enabled_primitives": sorted(enabled),
            "disabled_primitives": sorted(l1_config.get("disabled", [])),
            "weights": weights,
            "brief": brief_signals,
            "primitives": primitives,
        }

        logger.info(
            f"Resolved config for {business_id}: "
            f"sector={sector_code}, enabled={len(enabled)}, weights={len(weights)}"
        )

        return payload


# Convenience function
async def resolve_business_config(
    business_id: str,
    pool: asyncpg.Pool,
    mode: str | None = None,
) -> dict[str, Any] | None:
    """Resolve classification config for a business."""
    resolver = ConfigResolver()
    return await resolver.resolve(business_id, pool, mode)
