from __future__ import annotations
from dataclasses import dataclass, field
from typing import Callable, Any, List

class Validators:
    @staticmethod
    def required(value):
        ok = value is not None and str(value).strip() != ""
        return ok, "This field is required."

    @staticmethod
    def is_int(value):
        try:
            int(value)
            return True, ""
        except Exception:
            return False, "Expected integer."

    @staticmethod
    def is_float(value):
        try:
            float(value)
            return True, ""
        except Exception:
            return False, "Expected float."

    @staticmethod
    def email(value):
        ok = "@" in str(value) and "." in str(value)
        return ok, "Expected email address."

    @staticmethod
    def url(value):
        text = str(value)
        ok = text.startswith("http://") or text.startswith("https://")
        return ok, "Expected URL."

    @staticmethod
    def path(value):
        ok = str(value).strip() != ""
        return ok, "Expected path."

@dataclass
class FieldSpec:
    name: str
    field_type: str = "text"
    required: bool = False
    masked: bool = False
    conditional_on: str | None = None
    validators: List[Callable[[Any], tuple[bool, str]]] = field(default_factory=list)

class FormSchema:
    def __init__(self, fields):
        self.fields = list(fields)

    def validate(self, values):
        errors = {}
        for field in self.fields:
            value = values.get(field.name)
            if field.required:
                ok, msg = Validators.required(value)
                if not ok:
                    errors[field.name] = msg
                    continue
            for validator in field.validators:
                ok, msg = validator(value)
                if not ok:
                    errors[field.name] = msg
                    break
        return len(errors) == 0, errors
