from pydantic import Field, field_validator, model_validator

from .base import FrigateBaseModel

__all__ = ["AuthConfig"]


class AuthConfig(FrigateBaseModel):
    enabled: bool = Field(
        default=True,
        title="Enable authentication",
        description="Enable native authentication for the Frigate UI.",
    )
    reset_admin_password: bool = Field(
        default=False,
        title="Reset admin password",
        description="If true, reset the admin user's password on startup and print the new password in logs.",
    )
    cookie_name: str = Field(
        default="frigate_token",
        title="JWT cookie name",
        description="Name of the cookie used to store the JWT token for native authentication.",
        pattern=r"^[a-z_]+$",
    )
    cookie_secure: bool = Field(
        default=False,
        title="Secure cookie flag",
        description="Set the secure flag on the auth cookie; should be true when using TLS.",
    )
    session_length: int = Field(
        default=86400,
        title="Session length",
        description="Session duration in seconds for JWT-based sessions.",
        ge=60,
    )
    refresh_time: int = Field(
        default=1800,
        title="Session refresh window",
        description="When a session is within this many seconds of expiring, refresh it back to full length.",
        ge=30,
    )
    failed_login_rate_limit: str | None = Field(
        default=None,
        title="Failed login limits",
        description="Rate limiting rules for failed login attempts to reduce brute-force attacks.",
    )
    trusted_proxies: list[str] = Field(
        default=[],
        title="Trusted proxies",
        description="List of trusted proxy IPs used when determining client IP for rate limiting.",
    )
    # As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
    hash_iterations: int = Field(
        default=600000,
        title="Hash iterations",
        description="Number of PBKDF2-SHA256 iterations to use when hashing user passwords.",
    )
    roles: dict[str, list[str]] = Field(
        default_factory=dict,
        title="Role mappings",
        description="Map roles to camera lists. An empty list grants access to all cameras for the role.",
    )
    admin_first_time_login: bool | None = Field(
        default=False,
        title="First-time admin flag",
        description=(
            "When true the UI may show a help link on the login page informing users how to sign in after an admin password reset. "
        ),
    )

    @field_validator("roles")
    @classmethod
    def validate_roles(cls, v: dict[str, list[str]]) -> dict[str, list[str]]:
        # Ensure role names are valid (alphanumeric with underscores)
        for role in v.keys():
            if not role.replace("_", "").isalnum():
                raise ValueError(
                    f"Invalid role name '{role}'. Must be alphanumeric with underscores."
                )

        # Ensure 'admin' and 'viewer' are not used as custom role names
        reserved_roles = {"admin", "viewer"}
        if v.keys() & reserved_roles:
            raise ValueError(
                f"Reserved roles {reserved_roles} cannot be used as custom roles."
            )

        # Ensure no role has an empty camera list
        for role, allowed_cameras in v.items():
            if not allowed_cameras:
                raise ValueError(
                    f"Role '{role}' has no cameras assigned. Custom roles must have at least one camera."
                )

        return v

    @model_validator(mode="after")
    def ensure_default_roles(self):
        # Ensure admin and viewer are never overridden
        self.roles["admin"] = []
        self.roles["viewer"] = []

        return self
