"""
Product Capabilities Configuration Loader.

Defines what features are available per tier (the "packaging" truth).
Separate from enforcement limits in tier_limits.yaml.

This answers: "What can a user actually do, end-to-end?"
tier_limits.yaml answers: "How much can they do?"

Usage:
    from fmatch.saas.config.product_capabilities import CAPABILITIES_CONFIG

    # Get capability for a specific feature
    cap = CAPABILITIES_CONFIG.get_capability("gremlin_chat")

    # Check if a tier has access
    state = cap.tiers["free"].state  # "trial", "included", etc.
"""

from __future__ import annotations

import logging
from pathlib import Path
from typing import Any, Dict, List, Optional

import yaml
from pydantic import BaseModel, Field

logger = logging.getLogger(__name__)


# =============================================================================
# Configuration Models
# =============================================================================


class TierAvailability(BaseModel):
    """Availability state for a capability at a specific tier."""

    state: str = Field(..., pattern=r"^(included|limited|trial|addon|locked)$")
    trial_uses: Optional[int] = Field(default=None, ge=1)
    trial_period: Optional[str] = None
    limit_description: Optional[str] = None
    addon_description: Optional[str] = None
    upgrade_cta: Optional[str] = None


class Capability(BaseModel):
    """Definition of a product capability."""

    id: str
    label: str
    description: str
    group: str
    menu_path: Optional[str] = None
    core_value: bool = False
    shelved: bool = False
    tiers: Dict[str, TierAvailability]


class CapabilityGroup(BaseModel):
    """Group definition for organizing capabilities."""

    label: str
    description: str
    icon: str
    order: int
    parent: Optional[str] = None


class TrialConfig(BaseModel):
    """Trial tracking configuration."""

    tracking_mode: str = "lifetime"
    exhausted_error_code: str = "TRIAL_EXHAUSTED"
    default_upgrade_cta: str = "You've reached your free trial limit. Upgrade to continue."


class ProductCapabilitiesConfig(BaseModel):
    """Complete product capabilities configuration."""

    capabilities: Dict[str, Capability]
    groups: Dict[str, CapabilityGroup]
    free_aha_path: List[str] = Field(default_factory=list)
    trial_config: TrialConfig

    def get_capability(self, capability_id: str) -> Capability:
        """Get a specific capability by ID."""
        if capability_id not in self.capabilities:
            raise ValueError(
                f"Unknown capability: {capability_id}. "
                f"Available: {list(self.capabilities.keys())}"
            )
        return self.capabilities[capability_id]

    def get_capability_state(self, capability_id: str, tier: str) -> Optional[str]:
        """Get the availability state for a capability at a tier."""
        cap = self.get_capability(capability_id)
        tier_data = cap.tiers.get(tier)
        return tier_data.state if tier_data else None

    def is_available(self, capability_id: str, tier: str) -> bool:
        """Check if a capability is available (not locked) at a tier."""
        state = self.get_capability_state(capability_id, tier)
        return state is not None and state != "locked"

    def get_trial_uses(self, capability_id: str, tier: str) -> Optional[int]:
        """Get trial uses for a capability at a tier (if trial state)."""
        cap = self.get_capability(capability_id)
        tier_data = cap.tiers.get(tier)
        if tier_data and tier_data.state == "trial":
            return tier_data.trial_uses
        return None

    def get_capabilities_by_group(self, group_id: str) -> List[Capability]:
        """Get all capabilities in a group."""
        return [
            cap for cap in self.capabilities.values()
            if cap.group == group_id
        ]

    def get_aha_path_capabilities(self) -> List[Capability]:
        """Get capabilities in the free tier 'aha' path."""
        return [
            self.capabilities[cap_id]
            for cap_id in self.free_aha_path
            if cap_id in self.capabilities
        ]

    def to_api_response(self) -> Dict[str, Any]:
        """Serialize for API response."""
        return {
            "capabilities": {
                cap_id: {
                    "id": cap.id,
                    "label": cap.label,
                    "description": cap.description,
                    "group": cap.group,
                    "menu_path": cap.menu_path,
                    "core_value": cap.core_value,
                    "shelved": cap.shelved,
                    "tiers": {
                        tier_name: {
                            "state": tier_data.state,
                            "trial_uses": tier_data.trial_uses,
                            "trial_period": tier_data.trial_period,
                            "limit_description": tier_data.limit_description,
                            "addon_description": tier_data.addon_description,
                            "upgrade_cta": tier_data.upgrade_cta,
                        }
                        for tier_name, tier_data in cap.tiers.items()
                    }
                }
                for cap_id, cap in self.capabilities.items()
            },
            "groups": {
                group_id: {
                    "label": group.label,
                    "description": group.description,
                    "icon": group.icon,
                    "order": group.order,
                    "parent": group.parent,
                }
                for group_id, group in self.groups.items()
            },
            "free_aha_path": self.free_aha_path,
            "trial_config": {
                "tracking_mode": self.trial_config.tracking_mode,
                "exhausted_error_code": self.trial_config.exhausted_error_code,
                "default_upgrade_cta": self.trial_config.default_upgrade_cta,
            },
        }


# =============================================================================
# Config Loader
# =============================================================================


def load_product_capabilities_config(
    config_path: Optional[Path] = None,
) -> ProductCapabilitiesConfig:
    """
    Load product capabilities configuration from YAML file.

    Args:
        config_path: Path to YAML config file. If None, uses default location.

    Returns:
        Validated ProductCapabilitiesConfig instance

    Raises:
        FileNotFoundError: If config file doesn't exist
        ValueError: If config validation fails
    """
    if config_path is None:
        # Default location: config/product_capabilities.yaml from repo root
        repo_root = Path(__file__).parent.parent.parent.parent.parent
        config_path = repo_root / "config" / "product_capabilities.yaml"

    if not config_path.exists():
        raise FileNotFoundError(
            f"Product capabilities config not found at: {config_path}\n"
            f"Please create config/product_capabilities.yaml in the repo root."
        )

    logger.info(f"Loading product capabilities config from: {config_path}")

    with open(config_path) as f:
        data = yaml.safe_load(f)

    try:
        # Parse capabilities - need to add 'id' from the key
        raw_capabilities = data.get("capabilities", {})
        capabilities = {}
        for cap_id, cap_data in raw_capabilities.items():
            if "id" not in cap_data:
                cap_data["id"] = cap_id
            capabilities[cap_id] = Capability(**cap_data)

        config = ProductCapabilitiesConfig(
            capabilities=capabilities,
            groups={
                group_id: CapabilityGroup(**group_data)
                for group_id, group_data in data.get("groups", {}).items()
            },
            free_aha_path=data.get("free_aha_path", []),
            trial_config=TrialConfig(**data.get("trial_config", {})),
        )

        logger.info(
            f"Loaded product capabilities config: "
            f"{len(config.capabilities)} capabilities, {len(config.groups)} groups"
        )

        return config

    except Exception as e:
        logger.error(f"Failed to validate product capabilities config: {e}")
        raise ValueError(f"Invalid product capabilities config: {e}") from e


# =============================================================================
# Global Instance
# =============================================================================

# Load config at module import time
try:
    CAPABILITIES_CONFIG = load_product_capabilities_config()
except Exception as e:
    logger.warning(
        f"Failed to load product capabilities config at startup: {e}\n"
        f"Using None - endpoint will return 500 if accessed."
    )
    CAPABILITIES_CONFIG = None


def get_capability(capability_id: str) -> Capability:
    """
    Convenience function to get a capability configuration.

    Args:
        capability_id: ID of capability

    Returns:
        Capability config

    Raises:
        ValueError: If capability doesn't exist or config not loaded
    """
    if CAPABILITIES_CONFIG is None:
        raise ValueError(
            "Product capabilities config not loaded. "
            "Check config/product_capabilities.yaml"
        )
    return CAPABILITIES_CONFIG.get_capability(capability_id)


def check_capability_access(capability_id: str, tier: str) -> bool:
    """
    Check if a tier has access to a capability.

    Args:
        capability_id: ID of capability
        tier: Tier name (free, pro, scale, unleashed)

    Returns:
        True if accessible (not locked)
    """
    if CAPABILITIES_CONFIG is None:
        return False
    return CAPABILITIES_CONFIG.is_available(capability_id, tier)
