"""
Security configuration types for DAZZLE IR.

This module contains security-related IR types for configuring
application security behavior.
"""

from __future__ import annotations  # required: forward self-reference in class definitions

from enum import StrEnum

from pydantic import BaseModel, ConfigDict


class SecurityProfile(StrEnum):
    """
    Application security profile levels.

    All profiles include authentication and an admin workspace.
    The profile controls *how much* security infrastructure is generated,
    not whether auth exists.

    Profiles:
        BASIC: Prototyping and internal tools. Auth enabled but not required
            by default on surfaces. CORS allows all origins. Admin workspace
            includes: users, health, deploys, metrics.
        STANDARD: Production SaaS. Auth required by default, HSTS enabled,
            same-origin CORS. Admin workspace adds: sessions, processes.
        STRICT: Regulated / multi-tenant SaaS. Everything in STANDARD plus
            CSP headers and tenant database isolation (when multi_tenant=True).
    """

    BASIC = "basic"
    STANDARD = "standard"
    STRICT = "strict"


class TwoFactorMethod(StrEnum):
    """Supported two-factor authentication methods."""

    EMAIL_OTP = "email_otp"
    TOTP = "totp"


class TwoFactorConfig(BaseModel):
    """Two-factor authentication configuration.

    Attributes:
        enabled: Whether 2FA is available for users
        methods: Allowed 2FA methods
        otp_length: Number of digits in OTP codes
        otp_expiry_seconds: How long OTP codes remain valid
        recovery_code_count: Number of recovery codes to generate
        enforce_for_roles: Roles that must enable 2FA (empty = optional for all)
    """

    model_config = ConfigDict(frozen=True)

    enabled: bool = False
    methods: list[TwoFactorMethod] = [TwoFactorMethod.EMAIL_OTP, TwoFactorMethod.TOTP]
    otp_length: int = 6
    otp_expiry_seconds: int = 300
    recovery_code_count: int = 8
    enforce_for_roles: list[str] = []


class SecurityConfig(BaseModel):
    """
    Security configuration for the application.

    Derived from the security_profile setting and app configuration.

    Attributes:
        profile: The security profile level
        cors_origins: Allowed CORS origins (None = default based on profile)
        enable_hsts: Enable HTTP Strict Transport Security header
        enable_csp: Enable Content Security Policy header
        require_auth_by_default: Whether surfaces require auth unless opt-out
        tenant_isolation: Enable tenant database isolation
    """

    profile: SecurityProfile = SecurityProfile.BASIC
    cors_origins: list[str] | None = None
    enable_hsts: bool = False
    enable_csp: bool = False
    require_auth_by_default: bool = False
    tenant_isolation: bool = False

    model_config = ConfigDict(frozen=True)

    @classmethod
    def from_profile(
        cls,
        profile: SecurityProfile,
        *,
        multi_tenant: bool = False,
        cors_origins: list[str] | None = None,
    ) -> SecurityConfig:
        """
        Create SecurityConfig from a profile with sensible defaults.

        Args:
            profile: The security profile level
            multi_tenant: Whether the app is multi-tenant
            cors_origins: Custom CORS origins (overrides profile default)

        Returns:
            SecurityConfig with profile-based defaults
        """
        if profile == SecurityProfile.BASIC:
            return cls(
                profile=profile,
                cors_origins=cors_origins or ["*"],
                enable_hsts=False,
                enable_csp=False,
                require_auth_by_default=False,
                tenant_isolation=False,
            )
        elif profile == SecurityProfile.STANDARD:
            return cls(
                profile=profile,
                cors_origins=cors_origins,  # None = same-origin in production
                enable_hsts=True,
                enable_csp=False,  # CSP can break many apps
                require_auth_by_default=True,
                tenant_isolation=False,
            )
        else:  # STRICT
            return cls(
                profile=profile,
                cors_origins=cors_origins,  # None = same-origin only
                enable_hsts=True,
                enable_csp=True,
                require_auth_by_default=True,
                tenant_isolation=multi_tenant,
            )
