"""Schema validation rules."""

from __future__ import annotations

from typing import Any

from hatchdx.validator.models import ValidationResult, ValidationRule
from hatchdx.validator.registry import register_rule

# Valid top-level JSON Schema types.
_VALID_JSON_SCHEMA_TYPES = {"string", "number", "integer", "boolean", "array", "object", "null"}


@register_rule(
    name="tools_have_input_schema",
    category="schema",
    severity="error",
    description="All tools must have an inputSchema defined.",
)
def check_tools_have_input_schema(
    tools: list[Any],
    **kwargs: Any,
) -> list[ValidationResult]:
    """Check that every tool has a non-empty inputSchema."""
    rule: ValidationRule = check_tools_have_input_schema._rule  # type: ignore[attr-defined]
    results: list[ValidationResult] = []

    for tool in tools:
        schema = tool.input_schema
        if not schema:
            results.append(ValidationResult(
                rule=rule,
                passed=False,
                message=f"Tool '{tool.name}' has no inputSchema defined.",
                tool_name=tool.name,
            ))
        else:
            results.append(ValidationResult(
                rule=rule,
                passed=True,
                message=f"Tool '{tool.name}' has an inputSchema.",
                tool_name=tool.name,
            ))

    return results


@register_rule(
    name="parameters_have_descriptions",
    category="schema",
    severity="warning",
    description="All parameters in inputSchema should have a description field.",
)
def check_parameters_have_descriptions(
    tools: list[Any],
    **kwargs: Any,
) -> list[ValidationResult]:
    """Check that every property in the inputSchema has a description."""
    rule: ValidationRule = check_parameters_have_descriptions._rule  # type: ignore[attr-defined]
    results: list[ValidationResult] = []

    for tool in tools:
        schema = tool.input_schema
        properties = schema.get("properties", {})

        if not properties:
            # No properties to check — skip (the schema rule handles missing schemas).
            continue

        for param_name, param_def in properties.items():
            if not isinstance(param_def, dict):
                continue
            if "description" not in param_def or not param_def["description"]:
                results.append(ValidationResult(
                    rule=rule,
                    passed=False,
                    message=(
                        f"Parameter '{param_name}' in tool '{tool.name}' "
                        f"is missing a description."
                    ),
                    tool_name=tool.name,
                ))
            else:
                results.append(ValidationResult(
                    rule=rule,
                    passed=True,
                    message=(
                        f"Parameter '{param_name}' in tool '{tool.name}' "
                        f"has a description."
                    ),
                    tool_name=tool.name,
                ))

    return results


@register_rule(
    name="schema_types_valid",
    category="schema",
    severity="error",
    description="Schema property types must be valid JSON Schema types.",
)
def check_schema_types_valid(
    tools: list[Any],
    **kwargs: Any,
) -> list[ValidationResult]:
    """Check that property types in inputSchema are valid JSON Schema types."""
    rule: ValidationRule = check_schema_types_valid._rule  # type: ignore[attr-defined]
    results: list[ValidationResult] = []

    for tool in tools:
        schema = tool.input_schema
        properties = schema.get("properties", {})

        if not properties:
            continue

        for param_name, param_def in properties.items():
            if not isinstance(param_def, dict):
                continue

            param_type = param_def.get("type")
            if param_type is None:
                # No type specified — not necessarily invalid (could use $ref, oneOf, etc.)
                continue

            # type can be a string or a list of strings
            types_to_check = param_type if isinstance(param_type, list) else [param_type]

            for t in types_to_check:
                if t not in _VALID_JSON_SCHEMA_TYPES:
                    results.append(ValidationResult(
                        rule=rule,
                        passed=False,
                        message=(
                            f"Parameter '{param_name}' in tool '{tool.name}' "
                            f"has invalid type '{t}'."
                        ),
                        tool_name=tool.name,
                    ))
                else:
                    results.append(ValidationResult(
                        rule=rule,
                        passed=True,
                        message=(
                            f"Parameter '{param_name}' in tool '{tool.name}' "
                            f"has valid type '{t}'."
                        ),
                        tool_name=tool.name,
                    ))

    return results
