from django.conf import settings
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.core.validators import RegexValidator
from django.db import models
from django.utils import timezone
from django.utils.crypto import get_random_string


class CustomUserManager(BaseUserManager):
    def create_user(self, username, email, password=None, **extra_fields):
        if not username:
            raise ValueError("Users must have a username")
        if not email:
            raise ValueError("Users must have an email address")

        email = self.normalize_email(email)
        username = username.lower()

        user = self.model(username=username, email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, email, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        extra_fields.setdefault("is_active", True)

        if extra_fields.get("is_staff") is not True:
            raise ValueError("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        return self.create_user(username, email, password, **extra_fields)


class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    username = models.CharField(max_length=255, unique=True)
    first_name = models.CharField(max_length=100, blank=True)
    middle_name = models.CharField(max_length=100, blank=True)
    last_name = models.CharField(max_length=100, blank=True)

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_verified = models.BooleanField(default=False)
    is_celebrity = models.BooleanField(default=False)
    profile_completed = models.BooleanField(default=False)
    is_locked = models.BooleanField(default=False)
    account_locked_until = models.DateTimeField(null=True, blank=True)
    referral_code = models.CharField(max_length=64, unique=True, blank=True)
    referred_by = models.ForeignKey(
        'self',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='referrals',
    )

    date_joined = models.DateTimeField(default=timezone.now)
    has_set_password = models.BooleanField(default=False)

    objects = CustomUserManager()

    USERNAME_FIELD = "username"
    REQUIRED_FIELDS = ["email"]

    def __str__(self):
        return self.username

    def generate_referral_code(self):
        if not self.referral_code:
            base = self.username or get_random_string(8)
            code = base.replace(' ', '').lower()[:32]
            if not code:
                code = get_random_string(8)
            while CustomUser.objects.filter(referral_code=code).exists():
                code = f"{code}{get_random_string(4)}"
            self.referral_code = code

    def save(self, *args, **kwargs):
        self.generate_referral_code()
        super().save(*args, **kwargs)

    @property
    def full_name(self):
        return " ".join(name for name in [self.first_name, self.middle_name, self.last_name] if name)

    @property
    def is_account_locked(self):
        if self.account_locked_until and timezone.now() < self.account_locked_until:
            return True
        return self.is_locked

    def lock_account(self, until=None):
        self.is_locked = True
        self.account_locked_until = until
        self.save(update_fields=["is_locked", "account_locked_until"])

    def unlock_account(self):
        self.is_locked = False
        self.account_locked_until = None
        self.save(update_fields=["is_locked", "account_locked_until"])


class Profile(models.Model):
    class Visibility(models.TextChoices):
        PUBLIC = "public", "Public"
        DISCOVERABLE = "discoverable", "Discoverable"
        PRIVATE = "private", "Private"

    class Gender(models.TextChoices):
        FEMALE = "female", "Female"
        MALE = "male", "Male"
        NON_BINARY = "nonbinary", "Non-binary"
        OTHER = "other", "Other"
        PREFER_NOT_TO_SAY = "prefer_not_to_say", "Prefer not to say"

    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile")
    display_name = models.CharField(max_length=120, blank=True)
    headline = models.CharField(max_length=200, blank=True)
    bio = models.TextField(blank=True)
    pronouns = models.CharField(max_length=50, blank=True)
    gender = models.CharField(max_length=32, choices=Gender.choices, default=Gender.PREFER_NOT_TO_SAY)
    birth_date = models.DateField(null=True, blank=True)
    phone_regex = RegexValidator(
        regex=r"^\+?1?\d{9,15}$",
        message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.",
    )
    phone_number = models.CharField(validators=[phone_regex], max_length=17, blank=True)
    city = models.CharField(max_length=128, blank=True)
    state = models.CharField(max_length=128, blank=True)
    country = models.CharField(max_length=128, blank=True)
    latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
    longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
    timezone = models.CharField(max_length=64, blank=True)
    interests = models.JSONField(default=list, blank=True)
    social_links = models.JSONField(default=dict, blank=True)
    visibility = models.CharField(max_length=32, choices=Visibility.choices, default=Visibility.DISCOVERABLE)
    can_receive_messages = models.BooleanField(default=True)
    verification_requested_at = models.DateTimeField(null=True, blank=True)
    location_updated_at = models.DateTimeField(null=True, blank=True)
    updated_at = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    booking_fee = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

    class Meta:
        indexes = [
            models.Index(fields=["visibility"]),
            models.Index(fields=["city", "state", "country"]),
        ]

    def __str__(self):
        return self.display_name or self.user.username

    @property
    def age(self):
        if not self.birth_date:
            return None
        today = timezone.localdate()
        age = today.year - self.birth_date.year - ((today.month, today.day) < (self.birth_date.month, self.birth_date.day))
        return age

    @property
    def follower_count(self):
        return self.followers.count()

    @property
    def media_count(self):
        return self.media_items.filter(is_active=True).count()

    @property
    def is_adult(self):
        return self.age is not None and self.age >= 18

    def update_location(self, latitude, longitude):
        self.latitude = latitude
        self.longitude = longitude
        self.location_updated_at = timezone.now()
        self.save(update_fields=["latitude", "longitude", "location_updated_at"])

    def distance_to(self, latitude, longitude):
        if self.latitude is None or self.longitude is None:
            return None
        from math import acos, cos, radians, sin
        lat1, lng1, lat2, lng2 = map(radians, [float(self.latitude), float(self.longitude), latitude, longitude])
        earth_radius_km = 6371
        return earth_radius_km * acos(
            cos(lat1) * cos(lat2) * cos(lng2 - lng1) + sin(lat1) * sin(lat2)
        )


class IdentityVerification(models.Model):
    class Status(models.TextChoices):
        PENDING = "pending", "Pending"
        APPROVED = "approved", "Approved"
        REJECTED = "rejected", "Rejected"
        NEEDS_MORE_INFORMATION = "needs_more_information", "Needs more information"

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="verifications")
    status = models.CharField(max_length=32, choices=Status.choices, default=Status.PENDING)
    submitted_at = models.DateTimeField(auto_now_add=True)
    reviewed_at = models.DateTimeField(null=True, blank=True)
    rejection_reason = models.TextField(blank=True)
    government_id = models.FileField(upload_to="verification/documents/", null=True, blank=True)
    selfie_image = models.ImageField(upload_to="verification/selfies/", null=True, blank=True)
    additional_notes = models.TextField(blank=True)
    is_active = models.BooleanField(default=True)

    class Meta:
        ordering = ["-submitted_at"]

    def __str__(self):
        return f"Verification for {self.user.username} ({self.status})"
