"""
Laravel-style validation exception for Flask API with Pydantic
Similar to Laravel's ValidationException for consistent error handling
"""

from typing import Dict, List, Any, Optional
from pydantic import ValidationError
import json


class ValidationException(Exception):
    """
    Laravel-style validation exception that converts Pydantic validation errors
    to Laravel's format for consistent API responses.
    """

    def __init__(self, errors: Dict[str, List[str]], message: str = "The given data was invalid.", status_code: int = 422):
        """
        Initialize ValidationException

        Args:
            errors: Dictionary of field validation errors
            message: Main error message
            status_code: HTTP status code (default: 422)
        """
        self.errors = errors
        self.message = message
        self.status_code = status_code
        super().__init__(self.message)

    @classmethod
    def from_pydantic_error(cls, validation_error: ValidationError, message: str = "The given data was invalid.") -> 'ValidationException':
        """
        Convert Pydantic ValidationError to Laravel-style ValidationException

        Args:
            validation_error: Pydantic ValidationError instance
            message: Custom error message

        Returns:
            ValidationException instance
        """
        errors = {}

        for error in validation_error.errors():
            # Extract field path (handle nested fields)
            field_path = '.'.join(str(loc) for loc in error['loc'])

            # Get error message
            error_msg = error.get('msg', 'Validation failed')

            # Convert specific Pydantic error types to user-friendly messages
            error_type = error.get('type', '')
            if error_type == 'missing':
                error_msg = 'This field is required.'
            elif error_type == 'value_error':
                # Keep the custom message from field validators
                error_msg = error_msg
            elif error_type == 'type_error':
                expected_type = error.get('input_type', 'valid value')
                error_msg = f'This field must be a valid {expected_type}.'
            elif error_type in ['greater_than', 'greater_than_equal']:
                limit = error.get('ctx', {}).get('gt', error.get('ctx', {}).get('ge', ''))
                error_msg = f'This field must be greater than {limit}.'
            elif error_type in ['less_than', 'less_than_equal']:
                limit = error.get('ctx', {}).get('lt', error.get('ctx', {}).get('le', ''))
                error_msg = f'This field must be less than {limit}.'
            elif error_type == 'string_too_short':
                min_length = error.get('ctx', {}).get('min_length', '')
                error_msg = f'This field must be at least {min_length} characters.'
            elif error_type == 'string_too_long':
                max_length = error.get('ctx', {}).get('max_length', '')
                error_msg = f'This field must not exceed {max_length} characters.'

            # Add error to field
            if field_path not in errors:
                errors[field_path] = []
            errors[field_path].append(error_msg)

        return cls(errors, message)

    @classmethod
    def from_custom_errors(cls, errors: Dict[str, List[str]], message: str = "The given data was invalid.") -> 'ValidationException':
        """
        Create ValidationException from custom error dictionary

        Args:
            errors: Dictionary of field errors
            message: Main error message

        Returns:
            ValidationException instance
        """
        return cls(errors, message)

    def has_errors(self) -> bool:
        """Check if exception has any errors"""
        return bool(self.errors)

    def get_errors_for_field(self, field: str) -> List[str]:
        """Get errors for a specific field"""
        return self.errors.get(field, [])

    def add_error(self, field: str, error: str) -> None:
        """Add an error for a specific field"""
        if field not in self.errors:
            self.errors[field] = []
        self.errors[field].append(error)

    def to_dict(self) -> Dict[str, Any]:
        """Convert exception to dictionary for JSON serialization"""
        return {
            'message': self.message,
            'errors': self.errors
        }

    def to_json(self) -> str:
        """Convert exception to JSON string"""
        return json.dumps(self.to_dict(), ensure_ascii=False)

    def __str__(self) -> str:
        return f"ValidationException: {self.message} - Errors: {self.errors}"

    def __repr__(self) -> str:
        return f"ValidationException(message='{self.message}', errors={self.errors}, status_code={self.status_code})"
