"""Metric prefix routing based on model taxonomy.

This module determines the metric prefix (emms_model_*, emms_genai_*, emms_agentic_*, emms_vendor_*)
based on the two-field taxonomy: model_category + model_type.

The routing logic ensures metrics are properly categorized:
- vendor models → emms_vendor_* namespace (always)
- internal traditional ML → emms_model_* namespace
- internal generative AI → emms_genai_* namespace
- internal agentic AI → emms_agentic_* namespace

Note: This module returns the short prefix key ("model", "genai", "agentic", "vendor").
The full emms_ prefix is prepended by the calling code (e.g., core/client.py, integrations/genai.py).
"""

import logging
from typing import Literal, Tuple

logger = logging.getLogger(__name__)

# Type aliases for valid taxonomy values
ModelCategory = Literal["internal", "vendor"]
ModelType = Literal["regression", "time-series", "classification", "generative", "agentic"]

# Routing table: (model_category, model_type) → metric_prefix
# This table defines all valid taxonomy combinations and their routing
METRIC_PREFIX_ROUTING = {
    # Internal traditional ML models → emms_model_* namespace
    ("internal", "regression"): "model",
    ("internal", "time-series"): "model",
    ("internal", "classification"): "model",
    # Internal AI/LLM models → emms_genai_* namespace
    ("internal", "generative"): "genai",
    # Internal agentic AI models → emms_agentic_* namespace
    ("internal", "agentic"): "agentic",
    # Vendor models → emms_vendor_* namespace (always, regardless of type)
    ("vendor", "regression"): "vendor",
    ("vendor", "time-series"): "vendor",
    ("vendor", "classification"): "vendor",
    ("vendor", "generative"): "vendor",
    ("vendor", "agentic"): "vendor",
}


def get_metric_prefix(model_category: str, model_type: str) -> str:
    """Determine metric prefix based on two-field model taxonomy.

    This function implements the core routing logic for the MCA SDK. It maps
    the two-field taxonomy (model_category + model_type) to the appropriate
    metric namespace prefix.

    Routing Rules:
        - vendor + ANY type → "vendor" (vendor models always use vendor namespace)
        - internal + [regression, time-series, classification] → "model" (traditional ML)
        - internal + generative → "genai" (AI/LLM models)
        - internal + agentic → "agentic" (agentic AI models)

    Args:
        model_category: High-level model category ("internal" or "vendor")
        model_type: Specific model type (regression, time-series, classification,
                    generative, or agentic)

    Returns:
        Metric prefix string: "model", "genai", "agentic", or "vendor"

    Raises:
        ValueError: If the (model_category, model_type) combination is invalid
                    or not supported

    Examples:
        >>> get_metric_prefix("internal", "regression")
        'model'
        >>> get_metric_prefix("internal", "generative")
        'genai'
        >>> get_metric_prefix("vendor", "classification")
        'vendor'
        >>> get_metric_prefix("invalid", "regression")
        Traceback (most recent call last):
            ...
        ValueError: Invalid model taxonomy combination...

    See Also:
        - Story 2.4 acceptance criteria for routing requirements
        - OpenTelemetry semantic conventions for metric naming
    """
    key = (model_category, model_type)

    if key not in METRIC_PREFIX_ROUTING:
        raise ValueError(
            f"Invalid model taxonomy combination: "
            f"model_category='{model_category}', model_type='{model_type}'. "
            f"Valid model_category values: ['internal', 'vendor']. "
            f"Valid model_type values: ['regression', 'time-series', 'classification', "
            f"'generative', 'agentic']"
        )

    prefix = METRIC_PREFIX_ROUTING[key]
    logger.debug(
        "Routing taxonomy (%s, %s) → metric prefix '%s'", model_category, model_type, prefix
    )
    return prefix


def migrate_legacy_model_type(legacy_type: str) -> Tuple[str, str]:
    """Migrate legacy single-field model_type to two-field taxonomy.

    Provides backward compatibility for the old SDK usage where model_type
    was used for both category and type (e.g., model_type="internal").

    This function maps legacy values to the new two-field taxonomy by choosing
    sensible defaults for the model_type field.

    Legacy Migration Rules:
        - "internal" → ("internal", "regression")  # Default to traditional ML
        - "generative" → ("internal", "generative")  # Internal GenAI model
        - "vendor" → ("vendor", "regression")  # Default vendor to traditional ML

    Args:
        legacy_type: Old model_type value ("internal", "generative", or "vendor")

    Returns:
        Tuple of (model_category, model_type) for new taxonomy

    Raises:
        ValueError: If legacy_type is not a recognized legacy value

    Examples:
        >>> migrate_legacy_model_type("internal")
        ('internal', 'regression')
        >>> migrate_legacy_model_type("generative")
        ('internal', 'generative')
        >>> migrate_legacy_model_type("vendor")
        ('vendor', 'regression')
        >>> migrate_legacy_model_type("unknown")
        Traceback (most recent call last):
            ...
        ValueError: Invalid legacy model_type...

    Note:
        This function is used internally by MCAConfig for automatic migration.
        Deprecated usage will log warnings to guide users to the new taxonomy.
    """
    LEGACY_MIGRATION = {
        "internal": ("internal", "regression"),
        "generative": ("internal", "generative"),
        "vendor": ("vendor", "regression"),
    }

    if legacy_type not in LEGACY_MIGRATION:
        raise ValueError(
            f"Invalid legacy model_type: '{legacy_type}'. "
            f"Expected one of: {list(LEGACY_MIGRATION.keys())}. "
            f"If you're using the new taxonomy, ensure both model_category and "
            f"model_type are specified."
        )

    category, new_type = LEGACY_MIGRATION[legacy_type]
    logger.debug("Migrating legacy model_type='%s' → (%s, %s)", legacy_type, category, new_type)
    return category, new_type
