"""
Coercion functions for pre-validation data transformation.
These functions are applied before Pydantic validation (mode='before').
"""

from __future__ import annotations

from typing import Any, Callable

from pycharter.shared.coercions.builtin import (
    coerce_empty_to_null,
    coerce_to_boolean,
    coerce_to_date,
    coerce_to_datetime,
    coerce_to_float,
    coerce_to_integer,
    coerce_to_json,
    coerce_to_list,
    coerce_to_lowercase,
    coerce_to_none,
    coerce_to_nullable_boolean,
    coerce_to_nullable_datetime,
    coerce_to_nullable_float,
    coerce_to_nullable_integer,
    coerce_to_nullable_json,
    coerce_to_nullable_string,
    coerce_to_nullable_uuid,
    coerce_to_string,
    coerce_to_stripped_string,
    coerce_to_uppercase,
    coerce_to_uuid,
)

# Short descriptions for UI / API (Options > Coercion tab, docs)
COERCION_DESCRIPTIONS: dict[str, str | None] = {
    "coerce_to_string": "Convert value to string",
    "coerce_to_integer": 'Convert to int (handles "123", 123.0, bool)',
    "coerce_to_float": "Convert to float",
    "coerce_to_boolean": 'Convert to bool ("true"/"false", 0/1)',
    "coerce_to_datetime": "Parse datetime strings (ISO format)",
    "coerce_to_date": "Parse date strings",
    "coerce_to_uuid": "Parse UUID strings",
    "coerce_to_lowercase": "Convert string to lowercase",
    "coerce_to_uppercase": "Convert string to uppercase",
    "coerce_to_stripped_string": "Trim leading/trailing whitespace",
    "coerce_to_list": "Wrap single value in list",
    "coerce_empty_to_null": "Convert empty string to None",
    "coerce_to_none": "Always return None",
    "coerce_to_json": "Parse JSON string to object",
    "coerce_to_nullable_string": "Convert to string or None",
    "coerce_to_nullable_integer": "Convert to int or None",
    "coerce_to_nullable_float": "Convert to float or None",
    "coerce_to_nullable_boolean": "Convert to bool or None",
    "coerce_to_nullable_datetime": "Parse datetime or None",
    "coerce_to_nullable_uuid": "Parse UUID or None",
    "coerce_to_nullable_json": "Parse JSON or None",
}


def list_coercion_functions() -> list[dict[str, str]]:
    """Return list of supported coercion function names and descriptions for API/UI."""
    return [
        {"name": name, "description": COERCION_DESCRIPTIONS.get(name, "")}
        for name in sorted(COERCION_REGISTRY.keys())
    ]


# Registry of available coercion functions
COERCION_REGISTRY: dict[str, Callable[[Any], Any]] = {
    # Standard coercions
    "coerce_to_string": coerce_to_string,
    "coerce_to_integer": coerce_to_integer,
    "coerce_to_float": coerce_to_float,
    "coerce_to_boolean": coerce_to_boolean,
    "coerce_to_datetime": coerce_to_datetime,
    "coerce_to_date": coerce_to_date,
    "coerce_to_uuid": coerce_to_uuid,
    "coerce_to_lowercase": coerce_to_lowercase,
    "coerce_to_uppercase": coerce_to_uppercase,
    "coerce_to_stripped_string": coerce_to_stripped_string,
    "coerce_to_list": coerce_to_list,
    "coerce_empty_to_null": coerce_empty_to_null,
    # New coercions
    "coerce_to_none": coerce_to_none,
    "coerce_to_json": coerce_to_json,
    # Nullable variants (explicitly handle null values for optional fields)
    "coerce_to_nullable_string": coerce_to_nullable_string,
    "coerce_to_nullable_integer": coerce_to_nullable_integer,
    "coerce_to_nullable_float": coerce_to_nullable_float,
    "coerce_to_nullable_boolean": coerce_to_nullable_boolean,
    "coerce_to_nullable_datetime": coerce_to_nullable_datetime,
    "coerce_to_nullable_uuid": coerce_to_nullable_uuid,
    "coerce_to_nullable_json": coerce_to_nullable_json,
}


def get_coercion(name: str) -> Callable[[Any], Any] | None:
    """
    Get a coercion function by name.

    Args:
        name: Name of the coercion function

    Returns:
        The coercion function

    Raises:
        ValueError: If coercion function not found
    """
    if name not in COERCION_REGISTRY:
        raise ValueError(
            f"Coercion function '{name}' not found. "
            f"Available: {list(COERCION_REGISTRY.keys())}"
        )
    return COERCION_REGISTRY[name]


def register_coercion(name: str, func: Callable[[Any], Any | None]) -> None:
    """
    Register a custom coercion function.

    Args:
        name: Name to register the function under
        func: The coercion function
    """
    COERCION_REGISTRY[name] = func
