from __future__ import annotations

from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator


class FormModel(BaseModel):
    """Base Pydantic model for HTML form submissions."""

    model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)


class RegistrationForm(FormModel):
    username: str = Field(min_length=3, max_length=128)
    email: str = Field(min_length=5, max_length=255)
    password: str = Field(min_length=8, max_length=256)
    confirm_password: str

    @field_validator("email")
    @classmethod
    def validate_email(cls, value: str) -> str:
        if "@" not in value or value.startswith("@") or value.endswith("@"):
            raise ValueError("Invalid email address.")
        local, domain = value.split("@", 1)
        if not local or "." not in domain:
            raise ValueError("Invalid email address.")
        return value.lower()

    @field_validator("confirm_password")
    @classmethod
    def passwords_match(cls, value: str, info: ValidationInfo) -> str:
        password = info.data.get("password")
        if password != value:
            raise ValueError("Passwords do not match.")
        return value


class LoginForm(FormModel):
    username: str = Field(min_length=1, max_length=255)
    password: str = Field(min_length=1, max_length=256)


class PasswordResetRequestForm(FormModel):
    email: str = Field(min_length=5, max_length=255)

    @field_validator("email")
    @classmethod
    def validate_email(cls, value: str) -> str:
        if "@" not in value or value.startswith("@") or value.endswith("@"):
            raise ValueError("Invalid email address.")
        local, domain = value.split("@", 1)
        if not local or "." not in domain:
            raise ValueError("Invalid email address.")
        return value.lower()


class PasswordResetForm(FormModel):
    token: str = Field(min_length=1)
    password: str = Field(min_length=8, max_length=256)
    confirm_password: str

    @field_validator("confirm_password")
    @classmethod
    def reset_passwords_match(cls, value: str, info: ValidationInfo) -> str:
        password = info.data.get("password")
        if password != value:
            raise ValueError("Passwords do not match.")
        return value
