"""
User model and related functionality.
"""

from typing import List, Optional

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models import Q, UniqueConstraint
from django.utils import timezone

from django_cfg.modules.django_cleanup import delete_file

from ..managers.user_manager import UserManager
from .base import user_avatar_path


class CustomUser(AbstractUser):
    """Simplified user model for OTP-only authentication."""

    email = models.EmailField()

    # Profile fields
    first_name = models.CharField(max_length=50, blank=True)
    last_name = models.CharField(max_length=50, blank=True)
    company = models.CharField(max_length=100, blank=True)
    phone = models.CharField(max_length=20, blank=True)
    position = models.CharField(max_length=100, blank=True)
    language = models.CharField(max_length=10, blank=True, default='')
    avatar = models.ImageField(upload_to=user_avatar_path, blank=True, null=True)

    # Test account for App Store review, API testing, etc.
    # When enabled, OTP verification uses static code from settings.TEST_ACCOUNT_OTP
    is_test_account = models.BooleanField(
        default=False,
        help_text="Test account bypasses OTP with static code (for App Store review, etc.)"
    )

    # Account deletion (soft delete)
    # When set, account is deactivated and personal data anonymized
    deleted_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When set, account is considered deleted (soft delete)"
    )

    # Profile metadata
    updated_at = models.DateTimeField(auto_now=True)

    # Managers
    objects: UserManager = UserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username"]

    def __str__(self):
        return self.email

    def save(self, *args, **kwargs):
        """Ensure username is generated if empty."""
        if not self.username:
            self.username = self.__class__.objects._generate_unique_username()
        super().save(*args, **kwargs)

    @property
    def is_admin(self) -> bool:
        return self.is_superuser

    @property
    def full_name(self) -> str:
        """Get user's full name."""
        return self.__class__.objects.get_full_name(self)

    @property
    def initials(self) -> str:
        """Get user's initials for avatar fallback."""
        return self.__class__.objects.get_initials(self)

    @property
    def display_username(self) -> str:
        """Get formatted username for display."""
        return self.__class__.objects.get_display_username(self)

    @property
    def unanswered_messages_count(self) -> int:
        """Get count of unanswered messages for the user."""
        return self.__class__.objects.get_unanswered_messages_count(self)

    def get_sources(self) -> List['RegistrationSource']:
        """Get all sources associated with this user."""
        from .registration import RegistrationSource
        return RegistrationSource.objects.filter(user_registration_sources__user=self)

    @property
    def primary_source(self) -> Optional['RegistrationSource']:
        """Get the first source where user registered."""
        from .registration import UserRegistrationSource
        user_source = UserRegistrationSource.objects.filter(
            user=self,
            first_registration=True
        ).first()
        return user_source.source if user_source else None

    @property
    def avatar_url(self) -> Optional[str]:
        """
        Get avatar URL with fallback to OAuth provider avatar.

        Priority:
        1. Local uploaded avatar
        2. GitHub/OAuth provider avatar
        3. None

        Returns:
            Avatar URL string or None
        """
        # 1. Local avatar
        if self.avatar:
            return self.avatar.url

        # 2. OAuth provider avatar
        try:
            oauth_connection = self.oauth_connections.filter(
                provider_avatar_url__isnull=False
            ).exclude(
                provider_avatar_url=''
            ).first()

            if oauth_connection:
                return oauth_connection.provider_avatar_url
        except Exception:
            pass

        return None

    # === 2FA Properties ===

    @property
    def has_2fa_enabled(self) -> bool:
        """
        Check if user has active 2FA device.

        Returns:
            True if user has at least one active TOTP device
        """
        from django_cfg.apps.system.totp.services import TOTPService

        return TOTPService.has_active_device(self)

    @property
    def requires_2fa(self) -> bool:
        """
        Check if 2FA is required for this user based on global policy.

        Returns:
            True if 2FA is required for this user
        """
        from .settings import TwoFactorSettings

        settings = TwoFactorSettings.get_settings()
        return settings.user_requires_2fa(self)

    @property
    def should_prompt_2fa(self) -> bool:
        """
        Check if user should be prompted to enable 2FA.

        Returns:
            True if user should be prompted
        """
        from .settings import TwoFactorSettings

        if self.has_2fa_enabled:
            return False

        settings = TwoFactorSettings.get_settings()
        return settings.user_should_prompt_2fa(self)

    # === Account Deletion (Soft Delete) ===

    @property
    def is_deleted(self) -> bool:
        """Check if account is soft-deleted."""
        return self.deleted_at is not None

    def soft_delete(self) -> None:
        """
        Soft delete the account.

        Email is preserved intact — uniqueness is enforced only among active accounts
        via a partial unique constraint (unique_active_email). Multiple deleted accounts
        may share the same email address (historical archive).

        This will:
        1. Deactivate account (is_active = False)
        2. Anonymize personal data (GDPR compliance)
        3. Set deleted_at timestamp
        4. Delete avatar file
        """
        # 1. Deactivate
        self.is_active = False

        # 2. Anonymize personal data
        self.first_name = ""
        self.last_name = ""
        self.phone = ""
        self.company = ""
        self.position = ""

        # 3. Set deletion timestamp
        self.deleted_at = timezone.now()

        # 4. Delete avatar file (using django_storage module)
        delete_file(self.avatar)

        self.save()

    def restore(self) -> None:
        """
        Restore a soft-deleted account.

        Raises ValueError if the email is already taken by another active account.
        In that case, manually update self.email before calling restore(), or
        create a new account instead.
        """
        if not self.is_deleted:
            return

        # Check if email is available among active accounts
        if CustomUser.objects.filter(
            email=self.email, deleted_at__isnull=True
        ).exclude(pk=self.pk).exists():
            raise ValueError(
                f"Email {self.email} is already taken by an active account. "
                "Update self.email before restoring, or create a new account."
            )

        self.is_active = True
        self.deleted_at = None
        self.save()

    class Meta:
        app_label = 'django_cfg_accounts'
        verbose_name = "User"
        verbose_name_plural = "Users"
        constraints = [
            UniqueConstraint(
                fields=["email"],
                condition=Q(deleted_at__isnull=True),
                name="unique_active_email",
            )
        ]

    # Django system check silencing
    @classmethod
    def check(cls, **kwargs):
        errors = super().check(**kwargs)
        # Suppress auth.W004: USERNAME_FIELD='email' is not unique=True.
        # Uniqueness is intentionally enforced via partial constraint unique_active_email
        # (active accounts only) — soft-deleted accounts may share the same email (GDPR).
        return [e for e in errors if e.id != "auth.W004"]
