"""Shared flag validation and parsing utilities.

This module provides reusable validation functions for common flag patterns.
Import these in command handlers to avoid duplicating validation logic.

Usage:
    from cli.commands.flag_utils import validate_provider, validate_uuid, parse_tags

    def my_command(provider: str, id: str, tags: str | None) -> None:
        provider_enum = validate_provider(provider)
        uuid = validate_uuid(id, "resource")
        tag_list = parse_tags(tags)
"""

from uuid import UUID

import typer

from alloy_runtime_types.dtos.generation import REASONING_EFFORT_VALUES, ReasoningEffort
from alloy_runtime_types.dtos.knowledge import (
    SectionStrategyType,
    SourceType,
)
from alloy_runtime_types.enums.ownership_scope import OwnershipScope
from alloy_runtime_types.enums.provider import Provider
from alloy_runtime_types.enums.schema_enums import SchemaFormat
from alloy_runtime_types.enums.template_enums import (
    TemplateContentType,
    TemplateVisibility,
)


def validate_provider(provider: str) -> Provider:
    """Validate and convert provider string to Provider enum.

    Args:
        provider: Provider key string (e.g., 'openai', 'anthropic')

    Returns:
        Provider enum value

    Raises:
        typer.BadParameter: If provider is invalid
    """
    try:
        return Provider(provider.lower())
    except ValueError:
        valid = [p.value for p in Provider if not p.name.startswith("TESTING")]
        raise typer.BadParameter(
            f"Invalid provider '{provider}'. Valid: {', '.join(valid)}"
        )


def validate_uuid(value: str, name: str = "ID") -> UUID:
    """Validate and convert string to UUID.

    Args:
        value: UUID string
        name: Field name for error message (e.g., "agent", "template")

    Returns:
        UUID object

    Raises:
        typer.BadParameter: If UUID format is invalid
    """
    try:
        return UUID(value)
    except ValueError:
        raise typer.BadParameter(f"Invalid {name} UUID format: '{value}'")


def validate_scope(scope: str) -> OwnershipScope:
    """Validate and convert scope string to OwnershipScope enum.

    Args:
        scope: Scope string ('user', 'organization', 'global')

    Returns:
        OwnershipScope enum value

    Raises:
        typer.BadParameter: If scope is invalid
    """
    try:
        return OwnershipScope(scope.lower())
    except ValueError:
        valid = ", ".join(s.value for s in OwnershipScope)
        raise typer.BadParameter(f"Invalid scope '{scope}'. Valid: {valid}")


def validate_visibility(visibility: str) -> TemplateVisibility:
    """Validate and convert visibility string to TemplateVisibility enum.

    Args:
        visibility: Visibility string ('private', 'organization', 'public')

    Returns:
        TemplateVisibility enum value

    Raises:
        typer.BadParameter: If visibility is invalid
    """
    try:
        return TemplateVisibility(visibility.lower())
    except ValueError:
        valid = ", ".join(v.value for v in TemplateVisibility)
        raise typer.BadParameter(f"Invalid visibility '{visibility}'. Valid: {valid}")


def validate_template_content_type(content_type: str) -> TemplateContentType:
    """Validate and convert content type string to TemplateContentType enum.

    Args:
        content_type: Content type string ('system_instruction', 'user_message', 'fragment')

    Returns:
        TemplateContentType enum value

    Raises:
        typer.BadParameter: If content type is invalid
    """
    try:
        return TemplateContentType(content_type.lower())
    except ValueError:
        valid = ", ".join(ct.value for ct in TemplateContentType)
        raise typer.BadParameter(f"Invalid type '{content_type}'. Valid: {valid}")


def validate_schema_format(schema_format: str) -> SchemaFormat:
    """Validate and convert schema format string to SchemaFormat enum.

    Args:
        schema_format: Schema format string ('json_schema', 'regex')

    Returns:
        SchemaFormat enum value

    Raises:
        typer.BadParameter: If schema format is invalid
    """
    try:
        return SchemaFormat(schema_format.lower())
    except ValueError:
        valid = ", ".join(f.value for f in SchemaFormat)
        raise typer.BadParameter(
            f"Invalid schema format '{schema_format}'. Valid: {valid}"
        )


def validate_reasoning_effort(reasoning_effort: str) -> ReasoningEffort:
    lower = reasoning_effort.lower()
    if lower not in REASONING_EFFORT_VALUES:
        valid = ", ".join(REASONING_EFFORT_VALUES)
        raise typer.BadParameter(
            f"Invalid reasoning effort '{reasoning_effort}'. Valid: {valid}"
        )
    return lower


def validate_section_strategy(strategy: str) -> SectionStrategyType:
    """Validate section strategy for knowledge collection chunking.

    Args:
        strategy: Strategy string ('heading_based', 'paragraph_based', 'fixed_size', 'none')

    Returns:
        Valid SectionStrategyType value

    Raises:
        typer.BadParameter: If strategy is invalid
    """
    valid_strategies = ["heading_based", "paragraph_based", "fixed_size", "none"]
    lower = strategy.lower()
    if lower not in valid_strategies:
        raise typer.BadParameter(
            f"Invalid section strategy '{strategy}'. Valid: {', '.join(valid_strategies)}"
        )
    return lower  # type: ignore[return-value]


def validate_source_type(source_type: str) -> SourceType:
    """Validate source type for document ingestion.

    Args:
        source_type: Source type string ('upload', 'paste', 'api_import', 'url_scrape')

    Returns:
        Valid SourceType value

    Raises:
        typer.BadParameter: If source type is invalid
    """
    valid_types = ["upload", "paste", "api_import", "url_scrape"]
    lower = source_type.lower()
    if lower not in valid_types:
        raise typer.BadParameter(
            f"Invalid source type '{source_type}'. Valid: {', '.join(valid_types)}"
        )
    return lower  # type: ignore[return-value]


def validate_document_type(
    document_type: str,
) -> str:
    """Validate document type for preprocessing.

    Args:
        document_type: Document type string ('transcript', 'markdown', 'document', 'form')

    Returns:
        Valid document type string

    Raises:
        typer.BadParameter: If document type is invalid
    """
    valid_types = ["transcript", "markdown", "document", "form"]
    lower = document_type.lower()
    if lower not in valid_types:
        raise typer.BadParameter(
            f"Invalid document type '{document_type}'. Valid: {', '.join(valid_types)}"
        )
    return lower


def validate_output_mode(
    output_mode: str,
    output_file: str | None,
    valid_modes: tuple[str, ...] = ("console", "clipboard", "file"),
) -> None:
    """Validate output mode and file path combination.

    Args:
        output_mode: Output mode ('console', 'clipboard', 'file', 'editor')
        output_file: Output file path (required for 'file' mode)
        valid_modes: Tuple of valid mode strings

    Raises:
        typer.BadParameter: If mode is invalid or file required but not provided
    """
    if output_mode not in valid_modes:
        raise typer.BadParameter(
            f"Invalid output '{output_mode}'. Valid: {', '.join(valid_modes)}"
        )
    if output_mode == "file" and not output_file:
        raise typer.BadParameter("--output-file/-O required with --output file")


def parse_tags(tags: str | None) -> list[str]:
    """Parse comma-separated tags string into list.

    Args:
        tags: Comma-separated tags string or None

    Returns:
        List of tag strings (empty list if None)

    Example:
        parse_tags("email,marketing,draft") -> ["email", "marketing", "draft"]
        parse_tags(None) -> []
    """
    if not tags:
        return []
    return [t.strip() for t in tags.split(",") if t.strip()]


def parse_tags_required(tags: str) -> list[str]:
    """Parse comma-separated tags string, requiring at least one tag.

    Args:
        tags: Comma-separated tags string

    Returns:
        List of tag strings

    Raises:
        typer.BadParameter: If no valid tags after parsing
    """
    tag_list = parse_tags(tags)
    if not tag_list:
        raise typer.BadParameter("At least one tag required")
    return tag_list


def require_one_of(*args: tuple[str, object]) -> None:
    """Require that at least one of the named values is provided.

    Args:
        *args: Tuples of (name, value) to check

    Raises:
        typer.BadParameter: If all values are None/empty

    Example:
        require_one_of(
            ("--message", message),
            ("--message-template", message_template),
        )
    """
    provided = [name for name, value in args if value]
    if not provided:
        names = " or ".join(name for name, _ in args)
        raise typer.BadParameter(f"One of {names} is required")


def require_exclusive(*args: tuple[str, object]) -> None:
    """Require that at most one of the named values is provided.

    Args:
        *args: Tuples of (name, value) to check

    Raises:
        typer.BadParameter: If more than one value is provided

    Example:
        require_exclusive(
            ("--agent", agent),
            ("--provider/--model", provider or model),
        )
    """
    provided = [name for name, value in args if value]
    if len(provided) > 1:
        raise typer.BadParameter(f"Cannot use {' and '.join(provided)} together")


def require_together(*args: tuple[str, object]) -> None:
    """Require that if any of the named values is provided, all must be provided.

    Args:
        *args: Tuples of (name, value) to check

    Raises:
        typer.BadParameter: If some but not all values are provided

    Example:
        require_together(
            ("--provider", provider),
            ("--model", model),
        )
    """
    provided = [name for name, value in args if value]
    missing = [name for name, value in args if not value]

    if provided and missing:
        raise typer.BadParameter(
            f"{' and '.join(missing)} required with {' and '.join(provided)}"
        )


def check_no_update_flags(*args: tuple[str, object]) -> bool:
    """Check if no update flags were provided.

    Args:
        *args: Tuples of (name, value) to check

    Returns:
        True if no flags provided

    Example:
        if check_no_update_flags(("--name", name), ("--description", desc)):
            raise typer.BadParameter("At least one update flag required")
    """
    return all(value is None for _, value in args)


def require_update_flags(*args: tuple[str, object]) -> None:
    """Require that at least one update flag was provided.

    Args:
        *args: Tuples of (name, value) to check

    Raises:
        typer.BadParameter: If no flags provided
    """
    if check_no_update_flags(*args):
        names = ", ".join(name for name, _ in args)
        raise typer.BadParameter(f"At least one flag required: {names}")
