"""Constraint checkers for VibeDB schema validation."""

from typing import Any, List, Dict


class ConstraintChecker:
    """Base class for constraint checkers."""

    def check(self, value: Any, constraint_value: Any) -> bool:
        """
        Check if value satisfies the constraint.

        Args:
            value: Value to check
            constraint_value: Constraint parameter

        Returns:
            bool: True if constraint is satisfied
        """
        raise NotImplementedError

    def error_message(self, field: str, value: Any, constraint_value: Any) -> str:
        """
        Generate error message for constraint violation.

        Args:
            field: Field name
            value: Actual value
            constraint_value: Expected constraint

        Returns:
            str: Error message
        """
        raise NotImplementedError


class TypeConstraint(ConstraintChecker):
    """Type constraint checker."""

    TYPE_MAP = {
        'string': str,
        'integer': int,
        'number': (int, float),
        'boolean': bool,
        'array': list,
        'object': dict,
        'null': type(None),
    }

    def check(self, value: Any, expected_type: str) -> bool:
        """Check if value is of expected type."""
        if expected_type not in self.TYPE_MAP:
            return True  # Unknown type, skip validation

        python_type = self.TYPE_MAP[expected_type]
        return isinstance(value, python_type)

    def error_message(self, field: str, value: Any, expected_type: str) -> str:
        """Generate type error message."""
        actual_type = type(value).__name__
        return f"Field '{field}' must be {expected_type}, got {actual_type}"


class RequiredConstraint(ConstraintChecker):
    """Required field constraint."""

    def check(self, value: Any, required: bool) -> bool:
        """Check if required field is present."""
        # This is checked at document level, not value level
        return True

    def error_message(self, field: str, value: Any, required: bool) -> str:
        """Generate required error message."""
        return f"Field '{field}' is required"


class MinConstraint(ConstraintChecker):
    """Minimum value constraint (for numbers and strings)."""

    def check(self, value: Any, min_value: Any) -> bool:
        """Check if value >= min_value."""
        if isinstance(value, str):
            return len(value) >= min_value
        elif isinstance(value, (int, float)):
            return value >= min_value
        elif isinstance(value, list):
            return len(value) >= min_value
        return True

    def error_message(self, field: str, value: Any, min_value: Any) -> str:
        """Generate min error message."""
        if isinstance(value, str):
            return f"Field '{field}' must have at least {min_value} characters, got {len(value)}"
        elif isinstance(value, (int, float)):
            return f"Field '{field}' must be >= {min_value}, got {value}"
        elif isinstance(value, list):
            return f"Field '{field}' must have at least {min_value} items, got {len(value)}"
        return f"Field '{field}' must be >= {min_value}"


class MaxConstraint(ConstraintChecker):
    """Maximum value constraint (for numbers and strings)."""

    def check(self, value: Any, max_value: Any) -> bool:
        """Check if value <= max_value."""
        if isinstance(value, str):
            return len(value) <= max_value
        elif isinstance(value, (int, float)):
            return value <= max_value
        elif isinstance(value, list):
            return len(value) <= max_value
        return True

    def error_message(self, field: str, value: Any, max_value: Any) -> str:
        """Generate max error message."""
        if isinstance(value, str):
            return f"Field '{field}' must have at most {max_value} characters, got {len(value)}"
        elif isinstance(value, (int, float)):
            return f"Field '{field}' must be <= {max_value}, got {value}"
        elif isinstance(value, list):
            return f"Field '{field}' must have at most {max_value} items, got {len(value)}"
        return f"Field '{field}' must be <= {max_value}"


class EnumConstraint(ConstraintChecker):
    """Enum constraint - value must be in allowed list."""

    def check(self, value: Any, allowed_values: List[Any]) -> bool:
        """Check if value is in allowed list."""
        return value in allowed_values

    def error_message(self, field: str, value: Any, allowed_values: List[Any]) -> str:
        """Generate enum error message."""
        return f"Field '{field}' must be one of {allowed_values}, got {value}"


class PatternConstraint(ConstraintChecker):
    """Pattern constraint - value must match regex."""

    def check(self, value: Any, pattern: str) -> bool:
        """Check if string matches pattern."""
        import re
        if not isinstance(value, str):
            return False
        return bool(re.match(pattern, value))

    def error_message(self, field: str, value: Any, pattern: str) -> str:
        """Generate pattern error message."""
        return f"Field '{field}' must match pattern {pattern}, got '{value}'"


class UniqueConstraint(ConstraintChecker):
    """Unique constraint - value must be unique in collection."""

    def check(self, value: Any, unique: bool) -> bool:
        """Check is handled at collection level."""
        return True  # Checked separately

    def error_message(self, field: str, value: Any, unique: bool) -> str:
        """Generate unique error message."""
        return f"Field '{field}' must be unique, value '{value}' already exists"


class DefaultConstraint(ConstraintChecker):
    """Default value constraint."""

    def check(self, value: Any, default: Any) -> bool:
        """Default values don't need checking."""
        return True

    def error_message(self, field: str, value: Any, default: Any) -> str:
        """No error message for defaults."""
        return ""


# Constraint registry
CONSTRAINTS = {
    'type': TypeConstraint(),
    'required': RequiredConstraint(),
    'min': MinConstraint(),
    'max': MaxConstraint(),
    'enum': EnumConstraint(),
    'pattern': PatternConstraint(),
    'unique': UniqueConstraint(),
    'default': DefaultConstraint(),
}


def check_constraint(
    constraint_name: str,
    value: Any,
    constraint_value: Any,
    field: str = None
) -> tuple[bool, str]:
    """
    Check a constraint against a value.

    Args:
        constraint_name: Name of constraint (type, min, max, etc.)
        value: Value to check
        constraint_value: Constraint parameter
        field: Field name (for error messages)

    Returns:
        tuple: (is_valid, error_message)

    Example:
        >>> is_valid, error = check_constraint('type', 'hello', 'string')
        >>> assert is_valid
        >>> is_valid, error = check_constraint('min', 5, 10, 'age')
        >>> assert not is_valid
    """
    if constraint_name not in CONSTRAINTS:
        return True, ""  # Unknown constraint, skip

    checker = CONSTRAINTS[constraint_name]
    is_valid = checker.check(value, constraint_value)

    if not is_valid:
        error = checker.error_message(field or 'field', value, constraint_value)
        return False, error

    return True, ""
