"""
Сервисы для работы с платежными системами.
"""
from django.conf import settings
from django.utils import timezone
import requests
import logging

logger = logging.getLogger(__name__)


class PaymentService:
    """Сервис для работы с платежами."""
    
    def create_payment(self, payment, return_url=None):
        """
        Создать платеж в платежной системе.
        
        Args:
            payment: объект Payment
            return_url: URL для возврата после оплаты
        
        Returns:
            dict: данные платежа включая payment_url
        """
        if payment.payment_method == 'yookassa':
            return self._create_yookassa_payment(payment, return_url)
        elif payment.payment_method == 'stripe':
            return self._create_stripe_payment(payment, return_url)
        else:
            return {'payment_url': None}
    
    def _create_yookassa_payment(self, payment, return_url):
        """Создать платеж в ЮKassa."""
        try:
            from yookassa import Configuration, Payment as YooPayment
            
            Configuration.account_id = settings.YOOKASSA_SHOP_ID
            Configuration.secret_key = settings.YOOKASSA_SECRET_KEY
            
            # Создаем платеж
            yoo_payment = YooPayment.create({
                "amount": {
                    "value": str(payment.amount),
                    "currency": payment.currency
                },
                "confirmation": {
                    "type": "redirect",
                    "return_url": return_url or settings.FRONTEND_URL
                },
                "capture": True,
                "description": payment.description,
                "metadata": {
                    "payment_id": str(payment.uuid)
                }
            }, payment.uuid)
            
            # Сохраняем внешний ID
            payment.external_id = yoo_payment.id
            payment.status = 'processing'
            payment.provider_response = yoo_payment.json()
            payment.save()
            
            # Логируем историю
            from .models import PaymentHistory
            PaymentHistory.objects.create(
                payment=payment,
                status='processing',
                message='Платеж создан в ЮKassa',
                data=yoo_payment.json()
            )
            
            return {
                'payment_url': yoo_payment.confirmation.confirmation_url,
                'external_id': yoo_payment.id
            }
            
        except Exception as e:
            logger.error(f"Error creating YooKassa payment: {e}")
            payment.mark_as_failed(str(e))
            return {'payment_url': None, 'error': str(e)}
    
    def _create_stripe_payment(self, payment, return_url):
        """Создать платеж в Stripe."""
        try:
            import stripe
            
            stripe.api_key = settings.STRIPE_SECRET_KEY
            
            # Создаем сессию оплаты
            session = stripe.checkout.Session.create(
                payment_method_types=['card'],
                line_items=[{
                    'price_data': {
                        'currency': payment.currency.lower(),
                        'unit_amount': int(payment.amount * 100),  # в копейках
                        'product_data': {
                            'name': payment.subscription.plan.name,
                            'description': payment.description,
                        },
                    },
                    'quantity': 1,
                }],
                mode='payment',
                success_url=return_url or settings.FRONTEND_URL,
                cancel_url=return_url or settings.FRONTEND_URL,
                metadata={
                    'payment_id': str(payment.uuid)
                }
            )
            
            # Сохраняем внешний ID
            payment.external_id = session.id
            payment.status = 'processing'
            payment.provider_response = {'session_id': session.id}
            payment.save()
            
            # Логируем историю
            from .models import PaymentHistory
            PaymentHistory.objects.create(
                payment=payment,
                status='processing',
                message='Платеж создан в Stripe',
                data={'session_id': session.id}
            )
            
            return {
                'payment_url': session.url,
                'external_id': session.id
            }
            
        except Exception as e:
            logger.error(f"Error creating Stripe payment: {e}")
            payment.mark_as_failed(str(e))
            return {'payment_url': None, 'error': str(e)}
    
    def process_yookassa_webhook(self, data):
        """
        Обработать webhook от ЮKassa.
        
        Args:
            data: данные webhook
        
        Returns:
            dict: результат обработки
        """
        try:
            from .models import Payment, PaymentHistory
            
            event_type = data.get('event')
            payment_data = data.get('object', {})
            
            # Находим платеж
            payment_uuid = payment_data.get('metadata', {}).get('payment_id')
            if not payment_uuid:
                return {'success': False, 'error': 'payment_id not found'}
            
            try:
                payment = Payment.objects.get(uuid=payment_uuid)
            except Payment.DoesNotExist:
                return {'success': False, 'error': 'Payment not found'}
            
            # Обрабатываем событие
            if event_type == 'payment.succeeded':
                payment.mark_as_succeeded()
                
                PaymentHistory.objects.create(
                    payment=payment,
                    status='succeeded',
                    message='Платеж успешно выполнен',
                    data=data
                )
                
            elif event_type == 'payment.canceled':
                payment.mark_as_failed('Платеж отменен')
                
                PaymentHistory.objects.create(
                    payment=payment,
                    status='cancelled',
                    message='Платеж отменен',
                    data=data
                )
            
            return {'success': True}
            
        except Exception as e:
            logger.error(f"Error processing YooKassa webhook: {e}")
            return {'success': False, 'error': str(e)}
    
    def process_stripe_webhook(self, data):
        """
        Обработать webhook от Stripe.
        
        Args:
            data: данные webhook
        
        Returns:
            dict: результат обработки
        """
        try:
            from .models import Payment, PaymentHistory
            
            event_type = data.get('type')
            session_data = data.get('data', {}).get('object', {})
            
            # Находим платеж
            payment_uuid = session_data.get('metadata', {}).get('payment_id')
            if not payment_uuid:
                return {'success': False, 'error': 'payment_id not found'}
            
            try:
                payment = Payment.objects.get(uuid=payment_uuid)
            except Payment.DoesNotExist:
                return {'success': False, 'error': 'Payment not found'}
            
            # Обрабатываем событие
            if event_type == 'checkout.session.completed':
                payment.mark_as_succeeded()
                
                PaymentHistory.objects.create(
                    payment=payment,
                    status='succeeded',
                    message='Платеж успешно выполнен',
                    data=data
                )
                
            elif event_type == 'checkout.session.expired':
                payment.mark_as_failed('Сессия истекла')
                
                PaymentHistory.objects.create(
                    payment=payment,
                    status='failed',
                    message='Сессия истекла',
                    data=data
                )
            
            return {'success': True}
            
        except Exception as e:
            logger.error(f"Error processing Stripe webhook: {e}")
            return {'success': False, 'error': str(e)}


class PromoCodeService:
    """Сервис для работы с промокодами."""
    
    @staticmethod
    def validate_promo_code(code, user=None):
        """
        Валидация промокода.
        
        Args:
            code: код промокода
            user: пользователь (опционально)
        
        Returns:
            dict: {
                'valid': bool,
                'promo_code': PromoCode или None,
                'error': str или None
            }
        """
        from .models import PromoCode
        
        try:
            promo_code = PromoCode.objects.get(code=code.upper(), is_active=True)
        except PromoCode.DoesNotExist:
            return {
                'valid': False,
                'promo_code': None,
                'error': 'Промокод не найден'
            }
        
        is_valid, error = promo_code.is_valid(user)
        
        if not is_valid:
            return {
                'valid': False,
                'promo_code': promo_code,
                'error': error
            }
        
        return {
            'valid': True,
            'promo_code': promo_code,
            'error': None
        }
    
    @staticmethod
    def apply_promo_code(promo_code, amount):
        """
        Применить промокод к сумме.
        
        Args:
            promo_code: объект PromoCode
            amount: исходная сумма
        
        Returns:
            dict: {
                'original_amount': Decimal,
                'discount_amount': Decimal,
                'final_amount': Decimal
            }
        """
        discount_amount = promo_code.calculate_discount(amount)
        final_amount = max(amount - discount_amount, 0)
        
        return {
            'original_amount': amount,
            'discount_amount': discount_amount,
            'final_amount': final_amount
        }
    
    @staticmethod
    def increment_usage(promo_code):
        """Увеличить счетчик использований промокода."""
        promo_code.uses_count += 1
        promo_code.save(update_fields=['uses_count'])


class SubscriptionService:
    """Сервис для работы с подписками."""
    
    @staticmethod
    def get_active_subscription(user):
        """
        Получить активную подписку пользователя.
        
        Args:
            user: пользователь
        
        Returns:
            Subscription или None (только если подписка действительно активна)
        """
        from .models import Subscription
        
        # Получаем все подписки пользователя со статусом trial или active
        subscriptions = Subscription.objects.filter(
            user=user,
            status__in=['trial', 'active']
        ).order_by('-end_date')
        
        # Проверяем каждую подписку, пока не найдем действительно активную
        for subscription in subscriptions:
            # Проверяем, что подписка действительно активна (не истекла)
            if subscription.is_active():
                return subscription
        
        return None
    
    @staticmethod
    def get_subscription_or_expired(user):
        """
        Получить подписку пользователя (активную или истекшую).
        Используется для отображения информации о подписке, даже если она истекла.
        
        Args:
            user: пользователь
        
        Returns:
            Subscription или None
        """
        from .models import Subscription
        
        return Subscription.objects.filter(
            user=user,
            status__in=['trial', 'active', 'expired']
        ).order_by('-end_date').first()
    
    @staticmethod
    def calculate_subscription_price(plan, student_count=0, duration_days=30, promo_code=None, remaining_days=None, is_add_students=False, current_student_count=0):
        """
        Рассчитать цену подписки.
        
        Args:
            plan: SubscriptionPlan
            student_count: количество учеников (для типа per_student)
            duration_days: длительность в днях (30, 90, 180, 365)
            promo_code: промокод (опционально)
            remaining_days: оставшиеся дни текущей подписки (для продления)
            is_add_students: режим добавления учеников в текущий период
            current_student_count: текущее количество учеников в подписке (для расчета доплаты)
        
        Returns:
            dict: {
                'original_amount': Decimal,
                'discount_amount': Decimal,
                'final_amount': Decimal,
                'end_date': str,
                'end_date_display': str,
                'extra_students_payment': доплата за новых учеников
            }
        """
        return plan.calculate_price(
            student_count=student_count,
            duration_days=duration_days,
            promo_code=promo_code,
            remaining_days=remaining_days,
            is_add_students=is_add_students,
            current_student_count=current_student_count
        )
    
    @staticmethod
    def calculate_extra_students_payment(subscription, new_student_count):
        """
        Рассчитать доплату за дополнительных учеников.
        
        Args:
            subscription: Subscription объект
            new_student_count: новое общее количество учеников
        
        Returns:
            dict: {
                'extra_students': количество дополнительных учеников,
                'price_per_student': цена за одного дополнительного ученика,
                'days_remaining': оставшиеся дни подписки,
                'total_days': общее количество дней подписки,
                'payment_amount': сумма доплаты (пропорционально оставшимся дням),
                'next_month_amount': сумма за следующий месяц за всех учеников
            }
        """
        from django.utils import timezone
        from decimal import Decimal
        
        if subscription.plan.subscription_type != 'per_student':
            return {
                'extra_students': 0,
                'price_per_student': Decimal('0'),
                'days_remaining': 0,
                'total_days': subscription.duration_days,
                'payment_amount': Decimal('0'),
                'next_month_amount': Decimal('0')
            }
        
        # Текущее оплаченное количество
        paid_student_count = subscription.student_count
        
        # Количество дополнительных учеников
        extra_students = max(0, new_student_count - paid_student_count)
        
        if extra_students == 0:
            return {
                'extra_students': 0,
                'price_per_student': Decimal('0'),
                'days_remaining': 0,
                'total_days': subscription.duration_days,
                'payment_amount': Decimal('0'),
                'next_month_amount': Decimal('0')
            }
        
        # Рассчитываем цену за новое количество учеников
        # Используем calculate_price для получения правильной цены с учетом прогрессивных скидок
        price_data_new = subscription.plan.calculate_price(
            student_count=new_student_count,
            duration_days=30  # Рассчитываем за месяц
        )
        
        # Рассчитываем цену за старое количество учеников
        price_data_old = subscription.plan.calculate_price(
            student_count=paid_student_count,
            duration_days=30
        )
        
        # Цена за дополнительных учеников в месяц
        price_per_month_for_extra = price_data_new['final_amount'] - price_data_old['final_amount']
        
        # Цена за одного дополнительного ученика в месяц
        price_per_student_per_month = price_per_month_for_extra / Decimal(str(extra_students))
        
        # Оставшиеся дни подписки
        now = timezone.now()
        if now >= subscription.end_date:
            days_remaining = 0
        else:
            delta = subscription.end_date - now
            days_remaining = max(0, delta.days)
        
        # Пропорциональная доплата за оставшиеся дни
        total_days = subscription.duration_days
        if total_days > 0 and days_remaining > 0:
            # Рассчитываем пропорционально: (цена за месяц / 30 дней) * оставшиеся дни
            payment_amount = (price_per_student_per_month / Decimal('30')) * Decimal(str(days_remaining)) * Decimal(str(extra_students))
        else:
            payment_amount = Decimal('0')
        
        # Сумма за следующий месяц за всех учеников
        next_month_amount = price_data_new['final_amount']
        
        return {
            'extra_students': extra_students,
            'price_per_student': price_per_student_per_month,
            'days_remaining': days_remaining,
            'total_days': total_days,
            'payment_amount': payment_amount,
            'next_month_amount': next_month_amount,
            'current_student_count': paid_student_count,
            'new_student_count': new_student_count
        }
    
    @staticmethod
    def create_subscription(user, plan, student_count=0, duration_days=30, 
                          start_date=None, promo_code=None):
        """
        Создать подписку.
        
        Args:
            user: пользователь
            plan: SubscriptionPlan
            student_count: количество учеников (для типа per_student)
            duration_days: длительность в днях (30, 90, 180, 365)
            start_date: дата начала (если не указана - текущая дата)
            promo_code: промокод (опционально)
        
        Returns:
            Subscription
        """
        from .models import Subscription
        from django.utils import timezone
        from datetime import timedelta
        
        # Рассчитываем цену
        price_data = SubscriptionService.calculate_subscription_price(
            plan=plan,
            student_count=student_count,
            duration_days=duration_days,
            promo_code=promo_code
        )
        
        # Определяем даты
        if start_date:
            start = start_date
        else:
            start = timezone.now()
        
        end = start + timedelta(days=duration_days)
        
        # Создаем подписку
        subscription = Subscription.objects.create(
            user=user,
            plan=plan,
            student_count=student_count,
            duration_days=duration_days,
            start_date=start,
            end_date=end,
            promo_code=promo_code,
            original_amount=price_data['original_amount'],
            discount_amount=price_data['discount_amount'],
            final_amount=price_data['final_amount'],
            status='trial' if plan.trial_days > 0 else 'active'
        )
        
        # Увеличиваем счетчик использований промокода
        if promo_code:
            PromoCodeService.increment_usage(promo_code)
        
        return subscription
    
    @staticmethod
    def check_feature_access(user, feature_name):
        """
        Проверить доступ к функции.
        
        Args:
            user: пользователь
            feature_name: название функции
        
        Returns:
            bool
        """
        subscription = SubscriptionService.get_active_subscription(user)
        if not subscription:
            return False
        return subscription.has_feature(feature_name)
    
    @staticmethod
    def check_limit(user, limit_type):
        """
        Проверить лимит.
        
        Args:
            user: пользователь
            limit_type: тип лимита
        
        Returns:
            bool
        """
        subscription = SubscriptionService.get_active_subscription(user)
        if not subscription:
            return False
        return subscription.check_limit(limit_type)
    
    @staticmethod
    def log_usage(subscription, usage_type, amount, description=''):
        """
        Залогировать использование.
        
        Args:
            subscription: подписка
            usage_type: тип использования
            amount: количество
            description: описание
        """
        from .models import SubscriptionUsageLog
        
        # Создаем лог
        SubscriptionUsageLog.objects.create(
            subscription=subscription,
            usage_type=usage_type,
            amount=amount,
            description=description
        )
        
        # Обновляем счетчики
        if usage_type == 'lesson':
            subscription.lessons_used += amount
        elif usage_type == 'storage':
            subscription.storage_used_mb += amount
        elif usage_type == 'video_minutes':
            subscription.video_minutes_used += amount
        
        subscription.save()

    @staticmethod
    def change_plan(subscription, new_plan, student_count=None, duration_days=None, promo_code=None):
        """
        Сменить тарифный план подписки.
        
        Args:
            subscription: текущая подписка
            new_plan: новый тарифный план
            student_count: количество учеников (для типа per_student, если не указано - берется из текущей подписки)
            duration_days: длительность в днях (если не указана - используется из текущей подписки)
            promo_code: промокод (опционально)
        
        Returns:
            Subscription: обновленная подписка
        """
        from django.utils import timezone
        from datetime import timedelta
        from apps.users.models import Client
        
        # Определяем количество учеников
        if new_plan.subscription_type == 'per_student':
            if student_count is None:
                # Если переходим с ежемесячной на индивидуальную, нужно указать количество учеников
                if subscription.plan.subscription_type == 'monthly':
                    # Берем текущее количество клиентов пользователя
                    current_clients_count = Client.objects.filter(mentors=subscription.user).count()
                    if current_clients_count == 0:
                        raise ValueError('Необходимо указать количество учеников при переходе на тариф "За ученика"')
                    student_count = current_clients_count
                else:
                    # Переходим с индивидуальной на индивидуальную - берем из текущей подписки
                    student_count = subscription.student_count
        else:
            # Переходим на ежемесячную подписку - количество учеников не требуется
            student_count = 0
        
        # Определяем длительность
        if duration_days is None:
            duration_days = subscription.duration_days
        
        # Проверяем доступность периода для нового плана
        if not new_plan.is_duration_available(duration_days):
            available = new_plan.get_available_durations()
            raise ValueError(f'Период {duration_days} дней недоступен для этого тарифа. Доступные периоды: {", ".join(map(str, available))}')
        
        # Проверяем доступность тарифа
        can_use, error_message = new_plan.can_be_used(subscription.user)
        if not can_use:
            raise ValueError(error_message)
        
        # Рассчитываем цену для нового плана
        price_data = SubscriptionService.calculate_subscription_price(
            plan=new_plan,
            student_count=student_count,
            duration_days=duration_days,
            promo_code=promo_code,
            remaining_days=subscription.days_until_expiration() if subscription.is_active() else 0
        )
        
        # Обновляем подписку
        old_plan = subscription.plan
        subscription.plan = new_plan
        subscription.student_count = student_count
        subscription.duration_days = duration_days
        subscription.original_amount = price_data['original_amount']
        subscription.discount_amount = price_data['discount_amount']
        subscription.final_amount = price_data['final_amount']
        subscription.promo_code = promo_code
        
        # Если подписка активна, продлеваем её на новую длительность
        if subscription.is_active():
            remaining_days = subscription.days_until_expiration()
            if remaining_days > 0:
                # Добавляем новую длительность к оставшимся дням
                subscription.end_date = subscription.end_date + timedelta(days=duration_days)
            else:
                # Подписка истекла, начинаем с текущей даты
                subscription.start_date = timezone.now()
                subscription.end_date = timezone.now() + timedelta(days=duration_days)
        else:
            # Подписка неактивна, начинаем с текущей даты
            subscription.start_date = timezone.now()
            subscription.end_date = timezone.now() + timedelta(days=duration_days)
            subscription.status = 'active'
        
        subscription.save()
        
        # Увеличиваем счетчик использований тарифа, если это акция
        if new_plan.promo_type == 'limited_uses':
            new_plan.current_uses += 1
            new_plan.save(update_fields=['current_uses'])
        
        # Увеличиваем счетчик использований промокода
        if promo_code:
            PromoCodeService.increment_usage(promo_code)
        
        return subscription

