from .models import ShopOrderModel, ShopOrderItem, ShopDailyReport
from django.db import transaction
from django.db.models.signals import post_save
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import OrderModel
from account.models import PushSubscription, UserAddressModel
import ghasedak_sms
from .tasks import send_change_status_notif, send_change_status_sms, send_shop_order_invoice_telegram_task
from django.conf import settings
from decimal import Decimal


@receiver(pre_save, sender=OrderModel)
def order_status_changed(sender, instance, **kwargs):
    if instance.pk:
        previous = OrderModel.objects.get(pk=instance.pk)

        if previous.status != instance.status:
            new_status = instance.get_status_display()
            # send_change_status_notif.delay(instance.pk, new_status)
            # send_change_status_sms.delay(instance.pk, new_status)

        if previous.status == 'CART' and instance.status == 'ADMIN_PENDING':
            # update_cart_price_fields()
            # update_sell_data()
            # update_quantity()
            pass


@receiver(pre_save, sender=OrderModel)
def set_default_address(sender, instance, **kwargs):
    if instance.address is None and instance.user:
        default_address = UserAddressModel.objects.filter(
            user=instance.user, is_main=True).first()
        if default_address:
            instance.address = default_address


# def update_cart_price_fields(order):
#     pass

def update_sell_data(order):
    pass


def update_quantity(order):
    pass


@receiver(post_save, sender=OrderModel)
def create_shop_orders_on_payment(sender, instance: OrderModel, created, **kwargs):
    """When an order becomes paid, split it into per-shop ShopOrderModel records.

    This handler is safe to run multiple times (it checks if shop_orders exist).
    It triggers only when `is_paid` is True.
    """
    # Only generate when order is paid and we don't already have shop orders
    if not instance.is_paid:
        return

    if instance.shop_orders.exists():
        return

    # Collect order items grouped by shop
    items = instance.items.select_related('product__product__shop')

    shop_groups = {}
    for item in items:
        # product is ProductVariant -> product.product is ProductModel -> shop on ProductModel
        shop = None
        try:
            shop = item.product.product.shop
        except Exception:
            shop = None

        if not shop:
            # If product has no shop, skip (or you might want a default platform shop)
            continue

        shop_groups.setdefault(shop, []).append(item)

    if not shop_groups:
        return

    # Totals for proportional distribution (for cart-level discount and tax only)
    shop_subtotals = {}
    for shop, items_list in shop_groups.items():
        subtotal = 0
        for it in items_list:
            subtotal += int(it.price) * int(it.quantity)
        shop_subtotals[shop] = subtotal

    total_subtotal = sum(shop_subtotals.values()) or 1

    order_discount = int(instance.discount_amount or 0)
    order_tax = int(instance.tax or 0)

    with transaction.atomic():
        for shop, items_list in shop_groups.items():
            shop_subtotal = shop_subtotals.get(shop, 0)
            
            # Calculate total item-level discounts for this shop (discount_percent)
            item_level_discounts = Decimal('0')
            for it in items_list:
                item_discount = Decimal(str(it.price)) * Decimal(str(it.quantity)) * (Decimal(str(it.discount_percent or 0)) / Decimal('100'))
                item_level_discounts += item_discount
            
            # Calculate total item-level special discounts for this shop
            # (already calculated as profit * special_discount_percent)
            item_special_discounts = Decimal('0')
            for it in items_list:
                item_special_discounts += Decimal(str(it.special_discount_amount or 0))
            
            # Proportionally allocate cart-level discount
            allocated_discount = (Decimal(str(order_discount)) * Decimal(str(shop_subtotal)) / Decimal(str(total_subtotal))) if order_discount else Decimal('0')
            
            # Calculate tax on shop's amount after all discounts
            # Tax rate from settings (default 10%)
            tax_rate = Decimal(str(getattr(settings, 'DEFAULT_TAX_RATE', 10)))
            base_for_tax = max(
                Decimal('0'),
                Decimal(str(shop_subtotal)) - item_level_discounts - item_special_discounts - allocated_discount
            )
            allocated_tax = base_for_tax * (tax_rate / Decimal('100'))

            commission_percent = getattr(shop, 'commission_percent', 0) or 0
            try:
                commission_percent_value = Decimal(str(commission_percent))
            except Exception:
                commission_percent_value = Decimal('0')

            # Commission is calculated on the subtotal after ALL discounts:
            # subtotal - item_discount - item_special_discount - cart_discount
            base_for_commission = max(
                Decimal('0'), 
                Decimal(str(shop_subtotal)) - item_level_discounts - item_special_discounts - allocated_discount
            )
            commission_amount = base_for_commission * (commission_percent_value / Decimal('100'))

            # Payable to shop: subtotal minus all discounts minus commission (no tax added to payable)
            payable = Decimal(str(shop_subtotal)) - item_level_discounts - item_special_discounts - \
                allocated_discount - commission_amount

            # Prepare customer information
            customer_phone = (instance.user.phone or '') if instance.user else ''
            customer_name = (instance.user.full_name or '') if instance.user else ''

            # Prepare address information (with text backups in case address is deleted)
            address_text = ''
            address_postal_code = ''
            address_phone = ''
            address_city = ''
            address_province = ''
            address_recipient_name = ''
            
            if instance.address:
                address_text = instance.address.address or ''
                address_postal_code = instance.address.postal_code or ''
                address_phone = instance.address.phone or ''
                address_city = instance.address.city or ''
                address_province = instance.address.province or ''
                address_recipient_name = instance.address.name or ''

            # Convert Jalali date to datetime if needed
            order_created_datetime = None
            if instance.created_at:
                try:
                    # If it's already a datetime, use it
                    if hasattr(instance.created_at, 'hour'):
                        order_created_datetime = instance.created_at
                    else:
                        # If it's a date, convert to datetime at midnight
                        from datetime import datetime, time
                        order_created_datetime = datetime.combine(instance.created_at, time.min)
                except Exception:
                    order_created_datetime = None

            shop_order = ShopOrderModel.objects.create(
                order=instance,
                shop=shop,
                customer=instance.user,
                customer_phone=customer_phone,
                customer_name=customer_name,
                address=instance.address,
                address_text=address_text,
                address_postal_code=address_postal_code,
                address_phone=address_phone,
                address_city=address_city,
                address_province=address_province,
                address_recipient_name=address_recipient_name,
                status=instance.status,
                is_paid=instance.is_paid,
                subtotal=shop_subtotal,
                items_count=sum(int(it.quantity) for it in items_list),
                discount_amount=int(allocated_discount),
                special_discount_code=instance.special_discount_code,
                special_discount_amount=int(item_special_discounts),
                commission_percent=float(commission_percent_value),
                commission_amount=int(commission_amount),
                tax_amount=int(allocated_tax),
                payable_amount=int(payable),
                order_created_at=order_created_datetime,
            )

            # Create ShopOrderItem rows linking to original OrderItemModel
            for it in items_list:
                item_discount_amount = Decimal(str(it.price)) * Decimal(str(it.quantity)) * (Decimal(str(it.discount_percent or 0)) / Decimal('100'))
                ShopOrderItem.objects.create(
                    shop_order=shop_order,
                    order_item=it,
                    quantity=int(it.quantity),
                    unit_price=int(it.price),
                    total_price=int(it.price) * int(it.quantity),
                    discount_amount=int(item_discount_amount),
                    special_discount_amount=int(it.special_discount_amount or 0),
                )


@receiver(post_save, sender=ShopOrderModel)
def send_invoice_to_shop_telegram(sender, instance: ShopOrderModel, created, **kwargs):
    """Automatically send invoice to shop's Telegram chat when ShopOrderModel is created.
    
    This handler triggers when a new ShopOrderModel is created and the shop has a telegram_chat_id configured.
    It sends the invoice PDF asynchronously via Celery task.
    """
    if not created:
        return
    
    # Check if shop has telegram_chat_id configured
    if not instance.shop or not instance.shop.telegram_chat_id:
        return
    
    # Get bot token from settings
    bot_token = getattr(settings, 'TELEGRAM_BOT_TOKEN', None)
    if not bot_token:
        return
    
    # Send invoice asynchronously
    try:
        send_shop_order_invoice_telegram_task.delay(
            shop_order_id=instance.pk,
            chat_id=instance.shop.telegram_chat_id,
            bot_token=bot_token
        )
    except Exception as e:
        send_shop_order_invoice_telegram_task(
            shop_order_id=instance.pk,
            chat_id=instance.shop.telegram_chat_id,
            bot_token=bot_token
        )


@receiver(post_save, sender=ShopDailyReport)
def update_shop_orders_settlement_status(sender, instance: ShopDailyReport, **kwargs):
    """When a ShopDailyReport's is_settled status changes, update all related ShopOrderModel instances."""
    # Update all shop orders linked to this daily report
    instance.shop_orders.update(is_settled=instance.is_settled)
