"""
Caprail Form Request.

Provides validation and authorization for HTTP requests.

Usage::

    class StoreUserRequest(FormRequest):
        def authorize(self) -> bool:
            return True  # or check auth

        def rules(self) -> dict:
            return {
                "name": "required|string|max:255",
                "email": "required|email",
                "password": "required|min:8",
            }
"""
from __future__ import annotations

from caprail.http.request import Request


class ValidationError(Exception):
    def __init__(self, errors: dict[str, list[str]]) -> None:
        self.errors = errors
        super().__init__(str(errors))


class FormRequest(Request):
    """Extended Request with validation support."""

    def authorize(self) -> bool:
        return True

    def rules(self) -> dict[str, str]:
        return {}

    async def validate(self) -> dict:
        if not self.authorize():
            from caprail.http.exceptions import HttpException
            raise HttpException(403, "Unauthorized.")
        data = await self.all()
        errors: dict[str, list[str]] = {}
        for field, rule_str in self.rules().items():
            rules = rule_str.split("|")
            value = data.get(field)
            field_errors: list[str] = []
            for rule in rules:
                rule = rule.strip()
                if rule == "required" and not value:
                    field_errors.append(f"The {field} field is required.")
                elif rule == "email" and value and "@" not in str(value):
                    field_errors.append(f"The {field} must be a valid email address.")
                elif rule.startswith("min:") and value is not None:
                    min_len = int(rule[4:])
                    if len(str(value)) < min_len:
                        field_errors.append(f"The {field} must be at least {min_len} characters.")
                elif rule.startswith("max:") and value is not None:
                    max_len = int(rule[4:])
                    if len(str(value)) > max_len:
                        field_errors.append(f"The {field} may not be greater than {max_len} characters.")
                elif rule == "string" and value is not None and not isinstance(value, str):
                    field_errors.append(f"The {field} must be a string.")
                elif rule == "integer" and value is not None:
                    try:
                        int(value)
                    except (TypeError, ValueError):
                        field_errors.append(f"The {field} must be an integer.")
                elif rule == "boolean" and value is not None:
                    if str(value).lower() not in ("true", "false", "1", "0"):
                        field_errors.append(f"The {field} must be a boolean.")
                elif rule == "numeric" and value is not None:
                    try:
                        float(value)
                    except (TypeError, ValueError):
                        field_errors.append(f"The {field} must be numeric.")
            if field_errors:
                errors[field] = field_errors
        if errors:
            raise ValidationError(errors)
        return data
