"""
Validation Utilities

Input validation helpers for user inputs, API keys, and configuration.
"""

import re
from pathlib import Path
from typing import Literal

from honeydb_ai.domain.exceptions import ValidationError


def validate_api_key(api_key: str | None, provider: str) -> None:
    """
    Validate that an API key is present and appears valid.

    Args:
        api_key: API key to validate
        provider: Provider name (for error messages)

    Raises:
        ValidationError: If API key is invalid
    """
    if not api_key:
        raise ValidationError(
            f"{provider} API key is required",
            recovery_hint=f"Set your API key using: honeydb-ai config set {provider}_api_key YOUR_KEY"
        )

    if not isinstance(api_key, str) or len(api_key.strip()) == 0:
        raise ValidationError(
            f"{provider} API key cannot be empty",
            recovery_hint=f"Set a valid API key using: honeydb-ai config set {provider}_api_key YOUR_KEY"
        )

    # Basic format validation (most API keys are alphanumeric with some special chars)
    if not re.match(r'^[a-zA-Z0-9_\-\.]+$', api_key):
        raise ValidationError(
            f"{provider} API key contains invalid characters",
            recovery_hint="API keys typically contain only letters, numbers, hyphens, underscores, and dots"
        )


def validate_session_id(session_id: str) -> None:
    """
    Validate a session ID format.

    Args:
        session_id: Session ID to validate

    Raises:
        ValidationError: If session ID is invalid
    """
    if not session_id or not isinstance(session_id, str):
        raise ValidationError("Session ID cannot be empty")

    if len(session_id) < 8:
        raise ValidationError(
            "Session ID is too short",
            recovery_hint="Use 'honeydb-ai session list' to see valid session IDs"
        )

    # UUID format validation (optional - UUIDs are common but not required)
    # Allow alphanumeric with hyphens
    if not re.match(r'^[a-zA-Z0-9\-]+$', session_id):
        raise ValidationError(
            "Session ID contains invalid characters",
            recovery_hint="Session IDs should only contain letters, numbers, and hyphens"
        )


def validate_temperature(temperature: float) -> None:
    """
    Validate LLM temperature parameter.

    Args:
        temperature: Temperature value to validate

    Raises:
        ValidationError: If temperature is invalid
    """
    if not isinstance(temperature, (int, float)):
        raise ValidationError("Temperature must be a number")

    if temperature < 0.0 or temperature > 2.0:
        raise ValidationError(
            f"Temperature {temperature} is out of range",
            recovery_hint="Temperature must be between 0.0 and 2.0"
        )


def validate_max_tokens(max_tokens: int | None) -> None:
    """
    Validate LLM max_tokens parameter.

    This value is interpreted as the maximum number of *output* tokens
    the model is allowed to generate, not the total context window size.

    Args:
        max_tokens: Maximum number of output tokens to allow

    Raises:
        ValidationError: If max_tokens is invalid
    """
    if max_tokens is None:
        return  # None is valid (means no limit)

    if not isinstance(max_tokens, int):
        raise ValidationError("Max tokens must be an integer")

    if max_tokens < 1:
        raise ValidationError(
            "Max tokens must be at least 1",
            recovery_hint="Set max_tokens to a positive integer or None for no limit"
        )

    if max_tokens > 1_000_000:
        raise ValidationError(
            f"Max tokens {max_tokens} is unusually high for an output token limit",
            recovery_hint=(
                "max_tokens controls the number of output tokens the model may generate. "
                "Typical models support at most tens of thousands of output tokens. "
                "Check your specific model's maximum output token limit and choose a value within that range."
            ),
        )


def validate_provider(provider: str) -> None:
    """
    Validate LLM provider name.

    Args:
        provider: Provider name to validate

    Raises:
        ValidationError: If provider is invalid
    """
    valid_providers: list[Literal["openai", "anthropic", "gemini"]] = ["openai", "anthropic", "gemini"]

    if provider not in valid_providers:
        raise ValidationError(
            f"Invalid provider '{provider}'",
            recovery_hint=f"Valid providers are: {', '.join(valid_providers)}"
        )


def validate_model_name(model: str, provider: str) -> None:
    """
    Validate model name for a given provider.

    Args:
        model: Model name to validate
        provider: Provider name

    Raises:
        ValidationError: If model name is invalid
    """
    if not model or not isinstance(model, str) or len(model.strip()) == 0:
        raise ValidationError("Model name cannot be empty")

    # Provider-specific validation
    provider_prefixes = {
        "openai": ["gpt-", "text-", "davinci", "curie", "babbage", "ada"],
        "anthropic": ["claude-"],
        "gemini": ["gemini-", "models/gemini-"],
    }

    if provider in provider_prefixes:
        valid_prefixes = provider_prefixes[provider]
        if not any(model.startswith(prefix) for prefix in valid_prefixes):
            raise ValidationError(
                f"Model '{model}' doesn't appear to be a valid {provider} model",
                recovery_hint=f"Valid {provider} models typically start with: {', '.join(valid_prefixes)}"
            )


def validate_path(path: str | Path, must_exist: bool = False, must_be_dir: bool = False) -> Path:
    """
    Validate and expand a file path.

    Args:
        path: Path to validate
        must_exist: If True, path must exist
        must_be_dir: If True, path must be a directory

    Returns:
        Expanded Path object

    Raises:
        ValidationError: If path is invalid
    """
    if not path:
        raise ValidationError("Path cannot be empty")

    # Convert to Path and expand
    path_obj = Path(path).expanduser().resolve()

    if must_exist and not path_obj.exists():
        raise ValidationError(
            f"Path does not exist: {path_obj}",
            recovery_hint="Check that the path is correct and the file/directory exists"
        )

    if must_be_dir and path_obj.exists() and not path_obj.is_dir():
        raise ValidationError(
            f"Path is not a directory: {path_obj}",
            recovery_hint="Provide a directory path, not a file path"
        )

    return path_obj


def validate_log_level(log_level: str) -> None:
    """
    Validate logging level.

    Args:
        log_level: Log level to validate

    Raises:
        ValidationError: If log level is invalid
    """
    valid_levels: list[Literal["DEBUG", "INFO", "WARNING", "ERROR"]] = ["DEBUG", "INFO", "WARNING", "ERROR"]

    if log_level.upper() not in valid_levels:
        raise ValidationError(
            f"Invalid log level '{log_level}'",
            recovery_hint=f"Valid log levels are: {', '.join(valid_levels)}"
        )
