"""Custom user model for Telegram-authenticated accounts."""

from __future__ import annotations

import random
import string
from decimal import Decimal

from django.contrib.auth.models import (
    AbstractBaseUser,
    BaseUserManager,
    PermissionsMixin,
)
from django.db import models


class UserManager(BaseUserManager):
    """Manager for the custom User model keyed by Telegram user ID."""

    def create_user(
        self, telegram_user_id: int, password: str | None = None, **extra_fields
    ):
        if not telegram_user_id:
            raise ValueError("telegram_user_id must be provided")

        user = self.model(
            telegram_user_id=telegram_user_id,
            **extra_fields,
        )

        if not user.referral_code:
            user.referral_code = user.generate_referral_code()

        user.set_unusable_password()  # Telegram-only auth; no password flow.
        user.save(using=self._db)
        return user

    def create_superuser(
        self, telegram_user_id: int, password: str | None = 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(
            telegram_user_id=telegram_user_id,
            password=password,
            **extra_fields,
        )


class User(AbstractBaseUser, PermissionsMixin):
    telegram_user_id = models.BigIntegerField(unique=True, db_index=True)
    username = models.CharField(max_length=255, null=True, blank=True)
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255, null=True, blank=True)
    language_code = models.CharField(max_length=10, null=True, blank=True)
    photo_url = models.URLField(null=True, blank=True)

    credit_balance = models.DecimalField(
        max_digits=20,
        decimal_places=8,
        default=Decimal("0.00000000"),
    )
    bonus_balance = models.DecimalField(
        max_digits=20,
        decimal_places=8,
        default=Decimal("0.00000000"),
    )

    referral_code = models.CharField(max_length=10, unique=True, db_index=True)
    referred_by = models.ForeignKey(
        "self",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="referrals",
        db_constraint=False,  # Allows temporary references when user is not yet created.
    )

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    USERNAME_FIELD = "telegram_user_id"
    REQUIRED_FIELDS: list[str] = []

    objects = UserManager()

    class Meta:
        db_table = "users"
        indexes = [
            models.Index(fields=["telegram_user_id"]),
            models.Index(fields=["referral_code"]),
        ]

    def __str__(self):
        return f"{self.first_name} ({self.telegram_user_id})"

    def generate_referral_code(self) -> str:
        """Generate a unique 10-character referral code."""
        max_retries = 10
        alphabet = string.ascii_lowercase + string.digits

        for _ in range(max_retries):
            code = "".join(random.choices(alphabet, k=10))
            if not User.objects.filter(referral_code=code).exists():
                return code

        raise ValueError("Failed to generate unique referral code after 10 retries")

    def save(self, *args, **kwargs):
        if not self.referral_code:
            self.referral_code = self.generate_referral_code()
        super().save(*args, **kwargs)
