from decimal import Decimal

import phonenumbers
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Sum, Value, DecimalField as DjDecimalField
from django.db.models.functions import Coalesce
from phonenumber_field.modelfields import PhoneNumberField


class Customer(models.Model):
    """
    Модель клиента для цветочного магазина в Минске, Беларусь.
    """
    # Name field that is not required to be unique
    name = models.CharField(max_length=200, blank=True, verbose_name="Имя")

    email = models.EmailField(blank=True, null=True, verbose_name="Email")
    
    # Phone with validation using django-phonenumber-field
    phone = PhoneNumberField(
        blank=True,
        null=True,
        verbose_name="Телефон",
        help_text="Введите телефон в любом формате, например: +375291234567, 80255270546, 8(029)1234567 - будет автоматически преобразован в международный формат"
    )
    
    # Temporary field to store raw phone number during initialization
    _raw_phone = None

    # System customer flag
    is_system_customer = models.BooleanField(
        default=False,
        db_index=True,
        verbose_name="Системный клиент",
        help_text="Автоматически созданный клиент для анонимных покупок и наличных продаж"
    )

    # Additional notes
    notes = models.TextField(
        blank=True,
        null=True,
        verbose_name="Заметки",
        help_text="Заметки о клиенте, особые предпочтения и т.д."
    )
    

    
    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления")

    class Meta:
        verbose_name = "Клиент"
        verbose_name_plural = "Клиенты"
        indexes = [
            models.Index(fields=['name']),
            models.Index(fields=['email']),
            models.Index(fields=['phone']),
            models.Index(fields=['created_at']),
        ]
        ordering = ['-created_at']

    def __str__(self):
        if self.name:
            return self.name
        if self.phone:
            return str(self.phone)
        if self.email:
            return self.email
        return "Безымянный клиент"

    @property
    def full_name(self):
        """Полное имя клиента"""
        return self.name

    def clean_phone(self):
        """Пользовательская очистка поля телефона для нормализации перед валидацией."""
        if self.phone:
            try:
                # Парсим номер телефона, чтобы проверить его валидность и нормализовать
                raw_phone = str(self.phone)
                
                # Если начинается с '8' и имеет 11 цифр, это может быть российский формат
                if raw_phone.startswith('8') and len(raw_phone) == 11:
                    # Сначала пробуем BY для белорусских номеров
                    parsed = phonenumbers.parse(raw_phone, "BY")
                    if phonenumbers.is_valid_number(parsed):
                        return phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
                    
                    # Если BY не работает, пробуем RU как запасной вариант
                    parsed = phonenumbers.parse(raw_phone, "RU")
                    if phonenumbers.is_valid_number(parsed):
                        return phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
                
                # Пробуем парсить без кода страны (может быть уже в международном формате)
                parsed = phonenumbers.parse(raw_phone, None)
                if phonenumbers.is_valid_number(parsed):
                    return phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
                    
            except phonenumbers.NumberParseException:
                # Если парсинг не удался, возвращаем как есть и позволяем валидации поля обработать это
                pass
        
        return self.phone

    def clean_fields(self, exclude=None):
        # Нормализуем телефон перед запуском проверки полей
        if not exclude:
            exclude = []
        if 'phone' not in exclude and self.phone:
            normalized = None
            try:
                normalized = self.clean_phone()
            except Exception:
                normalized = None
            if normalized:
                # присваиваем нормализованное значение (E.164), чтобы PhoneNumberField видел корректный формат
                self.phone = normalized

        super().clean_fields(exclude=exclude)

    def clean(self):
        """Дополнительная валидация при необходимости."""
        super().clean()

    def save(self, *args, **kwargs):
        # Защита системного клиента от изменений
        if self.pk:
            # Получаем оригинальный объект из БД
            try:
                original = Customer.objects.get(pk=self.pk)
                # Проверяем, что это системный клиент в БД
                if original.is_system_customer:
                    # Проверяем, не пытаются ли изменить критичные поля
                    if original.email != self.email:
                        raise ValidationError("Нельзя изменить email системного клиента")
                    if original.is_system_customer != self.is_system_customer:
                        raise ValidationError("Нельзя изменить флаг системного клиента")
            except Customer.DoesNotExist:
                pass

        # Обеспечиваем нормализацию телефона, даже если save вызывается напрямую (не через форму)
        # На данный момент, если вызов прошел через валидацию формы, телефон уже должен быть нормализован
        # Но если save вызывается непосредственно в модели, нам все равно нужно нормализовать
        if self.phone and str(self.phone).startswith('8') and len(str(self.phone)) == 11:
            # Это, вероятно, номер в формате страны, который требует нормализации
            try:
                # Сначала пробуем BY для белорусских номеров
                parsed = phonenumbers.parse(str(self.phone), "BY")
                if phonenumbers.is_valid_number(parsed):
                    self.phone = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
                else:
                    # Если BY не работает, пробуем RU как запасной вариант
                    parsed = phonenumbers.parse(str(self.phone), "RU")
                    if phonenumbers.is_valid_number(parsed):
                        self.phone = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
            except phonenumbers.NumberParseException:
                # Если парсинг не удался, не меняем и позволяем валидации поля обработать это
                pass

        super().save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        """Защита системного клиента от удаления"""
        if self.is_system_customer:
            raise ValidationError("Нельзя удалить системного клиента. Он необходим для работы системы.")
        super().delete(*args, **kwargs)

    @classmethod
    def get_or_create_system_customer(cls):
        """
        Получить или создать системного клиента для анонимных покупок.

        Системный клиент используется для:
        - Анонимных покупок в POS системе
        - Покупок от неизвестных клиентов (проходимость)
        - Наличных продаж без указания покупателя

        Возвращает:
            tuple: (customer, created) - объект клиента и флаг создания
        """
        customer, created = cls.objects.get_or_create(
            email="system@pos.customer",
            defaults={
                "name": "АНОНИМНЫЙ ПОКУПАТЕЛЬ (POS)",
                "is_system_customer": True,
                "notes": "SYSTEM_CUSTOMER - автоматически созданный клиент для анонимных покупок и наличных продаж",
            }
        )
        return customer, created

    # Методы-обёртки для работы с кошельком (вся логика в WalletService)
    def pay_from_wallet(self, order, amount, user):
        """
        Оплатить заказ из кошелька клиента.
        Обёртка над WalletService.pay_with_wallet.

        Args:
            order: Заказ для оплаты
            amount: Сумма к списанию
            user: Пользователь, инициирующий операцию

        Returns:
            Decimal: Фактически списанная сумма или None
        """
        from customers.services.wallet_service import WalletService
        return WalletService.pay_with_wallet(order, amount, user)

    def adjust_wallet(self, amount, description, user):
        """
        Корректировка баланса кошелька (для админа).
        Обёртка над WalletService.adjust_balance.

        Args:
            amount: Сумма корректировки (может быть отрицательной)
            description: Обязательное описание причины
            user: Пользователь, выполняющий корректировку

        Returns:
            WalletTransaction: Созданная транзакция
        """
        from customers.services.wallet_service import WalletService
        return WalletService.adjust_balance(self.pk, amount, description, user)

    @property
    def wallet_transactions_history(self):
        """
        История транзакций кошелька клиента.

        Returns:
            QuerySet: WalletTransaction для этого клиента
        """
        return self.wallet_transactions.all()

    # ========== МЕТОДЫ ВЫЧИСЛЯЕМОГО БАЛАНСА КОШЕЛЬКА ==========

    def get_wallet_balance(self, category='money', use_cache=True):
        """
        Вычисляет баланс кошелька как SUM(signed_amount) транзакций.

        Args:
            category: 'money' или 'bonus' (для будущей бонусной системы)
            use_cache: использовать кеш (по умолчанию True)

        Returns:
            Decimal: текущий баланс
        """
        if not self.pk:
            return Decimal('0')

        cache_key = f'wallet_balance:{self.pk}:{category}'

        if use_cache:
            cached = cache.get(cache_key)
            if cached is not None:
                return Decimal(str(cached))

        result = self.wallet_transactions.filter(
            balance_category=category
        ).aggregate(
            total=Coalesce(
                Sum('signed_amount'),
                Value(0),
                output_field=DjDecimalField()
            )
        )

        balance = result['total'] or Decimal('0')

        if use_cache:
            cache.set(cache_key, str(balance), timeout=300)  # 5 минут

        return balance

    @property
    def wallet_balance(self):
        """
        Баланс кошелька (реальные деньги).
        Обратная совместимость: используется в templates и существующем коде.

        Returns:
            Decimal: текущий баланс кошелька
        """
        return self.get_wallet_balance(category='money')

    def invalidate_wallet_cache(self, category='money'):
        """Сбросить кеш баланса кошелька."""
        cache_key = f'wallet_balance:{self.pk}:{category}'
        cache.delete(cache_key)

    # Для будущей бонусной системы:
    # @property
    # def bonus_balance(self):
    #     """Баланс бонусных баллов."""
    #     return self.get_wallet_balance(category='bonus')

    def get_successful_orders_total(self, start_date=None, end_date=None):
        """
        Получить сумму успешных заказов за указанный период.

        Args:
            start_date: Дата начала периода (DateField или None)
            end_date: Дата окончания периода (DateField или None)

        Returns:
            Decimal: Сумма успешных заказов
        """
        from django.db.models import Sum, Value, DecimalField
        from django.db.models.functions import Coalesce
        from decimal import Decimal

        # Базовый queryset: только успешные заказы
        queryset = self.orders.filter(status__is_positive_end=True)

        # Фильтрация по датам (используем delivery__delivery_date после рефакторинга)
        if start_date:
            queryset = queryset.filter(delivery__delivery_date__gte=start_date)
        if end_date:
            queryset = queryset.filter(delivery__delivery_date__lte=end_date)

        # Агрегация суммы
        result = queryset.aggregate(
            total=Coalesce(
                Sum('total_amount'),
                Value(0),
                output_field=DecimalField()
            )
        )

        return result['total'] or Decimal('0')

    def get_last_year_orders_total(self):
        """
        Получить сумму успешных заказов за последний календарный год.
        (С этой даты прошлого года по текущую дату)

        Returns:
            Decimal: Сумма успешных заказов за год
        """
        from datetime import date, timedelta

        today = date.today()
        year_ago = today - timedelta(days=365)

        return self.get_successful_orders_total(start_date=year_ago, end_date=today)


class ContactChannel(models.Model):
    """Канал связи с клиентом (телефон, email, соцсети)"""

    CHANNEL_TYPES = [
        ('phone', 'Телефон'),
        ('email', 'Email'),
        ('telegram', 'Telegram'),
        ('instagram', 'Instagram'),
        ('whatsapp', 'WhatsApp'),
        ('viber', 'Viber'),
        ('vk', 'ВКонтакте'),
        ('facebook', 'Facebook'),
        ('other', 'Другое'),
    ]

    customer = models.ForeignKey(
        'Customer',
        on_delete=models.CASCADE,
        related_name='contact_channels',
        verbose_name="Клиент"
    )
    channel_type = models.CharField(
        max_length=20,
        choices=CHANNEL_TYPES,
        verbose_name="Тип канала"
    )
    value = models.CharField(
        max_length=255,
        verbose_name="Значение",
        help_text="Username, номер телефона, email и т.д."
    )
    is_primary = models.BooleanField(
        default=False,
        verbose_name="Основной"
    )
    notes = models.CharField(
        max_length=255,
        blank=True,
        verbose_name="Примечание"
    )
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")

    class Meta:
        unique_together = ['channel_type', 'value']
        indexes = [
            models.Index(fields=['channel_type', 'value']),
            models.Index(fields=['customer']),
        ]
        verbose_name = "Канал связи"
        verbose_name_plural = "Каналы связи"
        ordering = ['-is_primary', 'channel_type']

    def __str__(self):
        return f"{self.get_channel_type_display()}: {self.value}"


class WalletTransaction(models.Model):
    """
    Транзакция по кошельку клиента.
    Хранит историю всех пополнений, списаний и корректировок баланса.

    Архитектура: баланс кошелька = SUM(signed_amount) всех транзакций клиента.
    Это единственный источник правды о балансе.
    """

    # Типы транзакций (расширяемо для будущей бонусной системы)
    TRANSACTION_TYPE_CHOICES = [
        ('deposit', 'Пополнение'),
        ('spend', 'Списание'),
        ('adjustment', 'Корректировка'),
        # Для будущей бонусной системы:
        # ('bonus_accrual', 'Начисление бонусов'),
        # ('bonus_spend', 'Списание бонусов'),
        # ('cashback', 'Кэшбэк'),
    ]

    # Категории баланса (для разделения "реальные деньги" vs "бонусы")
    BALANCE_CATEGORY_CHOICES = [
        ('money', 'Реальные деньги'),
        # ('bonus', 'Бонусные баллы'),  # Для будущей реализации
    ]

    customer = models.ForeignKey(
        'Customer',
        on_delete=models.PROTECT,
        related_name='wallet_transactions',
        verbose_name="Клиент"
    )

    # Знаковая сумма: положительная = приход, отрицательная = расход
    signed_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name="Сумма",
        help_text="Положительная для пополнений, отрицательная для списаний"
    )

    transaction_type = models.CharField(
        max_length=20,
        choices=TRANSACTION_TYPE_CHOICES,
        verbose_name="Тип транзакции"
    )

    # Категория баланса (подготовка к бонусной системе)
    balance_category = models.CharField(
        max_length=20,
        choices=BALANCE_CATEGORY_CHOICES,
        default='money',
        verbose_name="Категория"
    )

    order = models.ForeignKey(
        'orders.Order',
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        related_name='wallet_transactions',
        verbose_name="Заказ",
        help_text="Заказ, к которому относится транзакция (если применимо)"
    )

    description = models.TextField(
        blank=True,
        verbose_name="Описание"
    )

    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name="Дата создания"
    )

    created_by = models.ForeignKey(
        'accounts.CustomUser',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name="Создано пользователем"
    )

    # Баланс после транзакции (для быстрого аудита и отображения в истории)
    balance_after = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True,
        verbose_name="Баланс после",
        help_text="Баланс кошелька после применения этой транзакции"
    )

    class Meta:
        verbose_name = "Транзакция кошелька"
        verbose_name_plural = "Транзакции кошелька"
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['customer', '-created_at']),
            models.Index(fields=['transaction_type']),
            models.Index(fields=['order']),
            models.Index(fields=['balance_category']),
            models.Index(fields=['customer', 'balance_category']),
        ]

    def __str__(self):
        sign = '+' if self.signed_amount >= 0 else ''
        return f"{self.get_transaction_type_display()} {sign}{self.signed_amount} руб. для {self.customer}"

    @property
    def amount(self):
        """Абсолютная сумма (для обратной совместимости)."""
        return abs(self.signed_amount)

