from ast import Or
import dis
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.core.exceptions import ValidationError
from decimal import Decimal
from django.template.loader import render_to_string
from django.core.files.base import ContentFile
from django.core.validators import MaxValueValidator, MinValueValidator, validate_email

try:
    from weasyprint import HTML
except (ImportError, OSError):
    HTML = None

import os


from configuration.models import SiteConfiguration

from thirdparty.zasilkovna.models import ZasilkovnaPacket
from thirdparty.stripe.models import StripeModel

from .tasks import notify_refund_accepted, notify_Ready_to_pickup, notify_zasilkovna_sended

#FIXME: přidat soft delete pro všchny modely !!!!

class Category(models.Model):
    name = models.CharField(max_length=100)

    #adresa kategorie např: /category/elektronika/mobily/
    url = models.SlugField(unique=True)

    #kategorie se můžou skládat pod sebe
    parent = models.ForeignKey(
        'self', null=True, blank=True, on_delete=models.CASCADE, related_name='subcategories'
    )

    description = models.TextField(blank=True)

    #ikona
    image = models.ImageField(upload_to='categories/', blank=True) 

    class Meta:
        verbose_name_plural = "Categories"

    def __str__(self):
        return self.name
    
#TODO: přidate brand model pro produkty (značky)

class Product(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField(blank=True)

    code = models.CharField(max_length=100, unique=True, blank=True, null=True)

    variants = models.ManyToManyField(
        "self",
        symmetrical=True,
        blank=True,
        help_text=(
            "Symetrické varianty produktu: pokud přidáte variantu A → B, "
            "Django automaticky přidá i variantu B → A. "
            "Všechny varianty jsou rovnocenné a zobrazí se vzájemně."
        ),
    )

    category = models.ForeignKey(Category, related_name='products', on_delete=models.PROTECT)

    # -- CENA --
    price = models.DecimalField(max_digits=10, decimal_places=2, help_text="Net price (without VAT)")
    # Currency is now global from SiteConfiguration, not per-product

    # VAT rate - configured by business owner in configuration app!!!
    vat_rate = models.ForeignKey(
        'configuration.VATRate',
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        help_text="VAT rate for this product. Leave empty to use default rate."
    )

    url = models.SlugField(unique=True)

    stock = models.PositiveIntegerField(default=0)
    is_active = models.BooleanField(default=True)

    #časový limit (volitelné)
    limited_to = models.DateTimeField(null=True, blank=True)

    #TODO: delete
    default_carrier = models.ForeignKey(
        "Carrier", on_delete=models.SET_NULL, null=True, blank=True, related_name="default_for_products"
    )

    include_in_week_summary_email = models.BooleanField(default=False)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    @property
    def available(self):
        return self.is_active and self.stock > 0

    def get_vat_rate(self):
        """Get the VAT rate for this product (from configuration or default)"""
        if self.vat_rate:
            return self.vat_rate
        # Import here to avoid circular imports
        from configuration.models import VATRate
        return VATRate.get_default()

    def get_price_with_vat(self):
        """Get price including VAT"""
        vat_rate = self.get_vat_rate()
        if not vat_rate:
            return self.price  # No VAT configured
        return self.price * (Decimal('1') + vat_rate.rate_decimal)

    def get_vat_amount(self):
        """Get the VAT amount for this product"""
        vat_rate = self.get_vat_rate()
        if not vat_rate:
            return Decimal('0')
        return self.price * vat_rate.rate_decimal

    def __str__(self):
        config = SiteConfiguration.get_solo()
        return f"{self.name} ({self.get_price_with_vat()} {config.currency} inkl. MwSt)"
    
#obrázek pro produkty
class ProductImage(models.Model):
    product = models.ForeignKey(Product, related_name='images', on_delete=models.CASCADE)

    image = models.ImageField(upload_to='products/')

    alt_text = models.CharField(max_length=150, blank=True)
    is_main = models.BooleanField(default=False)
    order = models.PositiveIntegerField(default=0, help_text="Display order (lower numbers first)")

    class Meta:
        ordering = ['order', '-is_main', 'id']

    def __str__(self):
        return f"{self.product.name} image"



# ------------------ OBJEDNÁVKY ------------------

class Order(models.Model):
    class OrderStatus(models.TextChoices):
        CREATED = "created", "Vytvořeno"
        CANCELLED = "cancelled", "Zrušeno"
        COMPLETED = "completed", "Dokončeno"

        REFUNDING = "refunding", "Vrácení v procesu"
        REFUNDED = "refunded", "Vráceno"

    status = models.CharField(
        max_length=20, choices=OrderStatus.choices, null=True, blank=True, default=OrderStatus.CREATED
    )

    # Stored order grand total; recalculated on save
    total_price = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00'))

    # Currency - captured from site configuration at creation time, never changes
    currency = models.CharField(max_length=10, default="", help_text="Order currency - captured from site configuration at order creation and never changes")

    # fakturační údaje (zkopírované z user profilu při objednávce)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, related_name="orders", null=True, blank=True
    )

    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    email = models.EmailField()
    phone = models.CharField(max_length=20, blank=True)
    address = models.CharField(max_length=255)
    city = models.CharField(max_length=100)
    postal_code = models.CharField(max_length=20)
    country = models.CharField(max_length=100, default="Czech Republic")

    note = models.TextField(blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    carrier = models.OneToOneField(
        "Carrier",
        on_delete=models.CASCADE,
        related_name="order",
        null=True,
        blank=True
    )

    payment = models.OneToOneField(
        "Payment",
        on_delete=models.CASCADE,
        related_name="order",
        null=True,
        blank=True
    )

    invoice = models.OneToOneField("Invoice", on_delete=models.CASCADE, related_name="order", null=True, blank=True)

    #FIXME: změnnit název na discount_code
    discount = models.ManyToManyField("DiscountCode", blank=True, related_name="orders")
    
    def calculate_total_price(self):
        carrier_price = self.carrier.get_price() if self.carrier else Decimal("0.0")

        # Check if order has been saved (has an ID) before accessing many-to-many relationships
        if self.pk and self.discount.exists():
            discounts = list(self.discount.all())

            total = Decimal('0.0')

            # getting all prices from order items (with discount applied if valid)
            for item in self.items.all():
                total = total + item.get_total_price(discounts)

            return total + carrier_price
        
        else:
            total = Decimal('0.0')
            # getting all prices from order items (without discount) - using VAT-inclusive prices
            
            # Only try to access items if order has been saved
            if self.pk:
                for item in self.items.all():
                    total = total + (item.product.get_price_with_vat() * item.quantity)

            return total + carrier_price

    def import_data_from_user(self):
        """Import user data into order for billing purposes."""
        self.first_name = self.user.first_name
        self.last_name = self.user.last_name
        self.email = self.user.email
        self.phone = self.user.phone
        self.address = f"{self.user.street} {self.user.street_number}"
        self.city = self.user.city
        self.postal_code = self.user.postal_code
        self.country = self.user.country

    def clean(self):
        """Validate order data"""
        # Validate required fields
        required_fields = ['first_name', 'last_name', 'email', 'address', 'city', 'postal_code']
        for field in required_fields:
            if not getattr(self, field):
                raise ValidationError(f"{field.replace('_', ' ').title()} is required.")
        
        # Validate email format
        try:
            validate_email(self.email)
        except ValidationError:
            raise ValidationError("Invalid email format.")
        
        # Validate order has items
        if self.pk and not self.items.exists():
            raise ValidationError("Order must have at least one item.")
    
    def get_currency(self):
        """Get order currency - falls back to site configuration if not set"""
        if self.currency:
            return self.currency
        config = SiteConfiguration.get_solo()
        return config.currency

    def save(self, *args, **kwargs):
        is_new = self.pk is None
        old_status = None
        
        # Track old status for change detection
        if not is_new:
            try:
                old_instance = Order.objects.get(pk=self.pk)
                old_status = old_instance.status
            except Order.DoesNotExist:
                pass

        # CRITICAL: Set currency from site configuration ONLY at creation time
        # Once set, currency should NEVER change to maintain order integrity
        if is_new and not self.currency:
            config = SiteConfiguration.get_solo()
            self.currency = config.currency

        # Keep total_price always in sync with items and discount
        self.total_price = self.calculate_total_price()
        if self.user and is_new:
            self.import_data_from_user()
        
        super().save(*args, **kwargs)

        # Send email notification for new orders
        if is_new:
            from .tasks import notify_order_successfuly_created
            notify_order_successfuly_created.delay(order=self)
        
        # Send email notification when status changes to CANCELLED
        if not is_new and old_status != self.OrderStatus.CANCELLED and self.status == self.OrderStatus.CANCELLED:
            from .tasks import notify_order_cancelled
            notify_order_cancelled.delay(order=self)
        
        # Send email notification when status changes to COMPLETED
        if not is_new and old_status != self.OrderStatus.COMPLETED and self.status == self.OrderStatus.COMPLETED:
            from .tasks import notify_order_completed
            notify_order_completed.delay(order=self)

    def cancel_order(self):
        """Cancel the order if possible"""
        if self.status == self.OrderStatus.CREATED:
            self.status = self.OrderStatus.CANCELLED
            self.save()
            #TODO: udělat ještě kontrolu jestli už nebyla odeslána zásilka a pokud bude už zaplacena tak se uděla refundace a pokud nebude zaplacena tak se zruší brána.
        else:
            raise ValidationError("Only orders in 'created' status can be cancelled.")


# ------------------ DOPRAVCI A ZPŮSOBY DOPRAVY ------------------

class Carrier(models.Model):
    class SHIPPING(models.TextChoices):
        ZASILKOVNA = "packeta", "Zásilkovna"
        DEUTSCHEPOST = "deutschepost", "Deutsche Post"
        STORE = "store", "Osobní odběr"
    shipping_method = models.CharField(max_length=20, choices=SHIPPING.choices, default=SHIPPING.STORE)

    class STATE(models.TextChoices):
        PREPARING = "ordered", "Objednávka se připravuje"
        SHIPPED = "shipped", "Odesláno"
        DELIVERED = "delivered", "Doručeno"
        READY_TO_PICKUP = "ready_to_pickup", "Připraveno k vyzvednutí"
        #RETURNING = "returning", "Vracení objednávky"
    state = models.CharField(max_length=20, choices=STATE.choices, default=STATE.PREPARING)

    # prodejce to přidá později 
    zasilkovna = models.ManyToManyField(
        ZasilkovnaPacket, blank=True, related_name="carriers"
    )
    
    # Deutsche Post integration (same pattern as zasilkovna)
    deutschepost = models.ManyToManyField(
        "deutschepost.DeutschePostOrder", blank=True, related_name="carriers"
    )

    weight = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, help_text="Hmotnost zásilky v kg")

    returning = models.BooleanField(default=False, help_text="Zda je tato zásilka na vrácení")
    
    shipping_price = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00'))

    def save(self, *args, **kwargs):
        # Set shipping price for new carriers
        if self.pk is None and self.shipping_price is None:
            # For new carriers, we might not have an order yet
            self.shipping_price = self.get_price(order=None)

        # Check if state changed to ready for pickup
        old_state = None
        if self.pk:
            old_carrier = Carrier.objects.filter(pk=self.pk).first()
            old_state = old_carrier.state if old_carrier else None

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

        # Send notification if state changed to ready for pickup
        if (old_state != self.STATE.READY_TO_PICKUP and 
            self.state == self.STATE.READY_TO_PICKUP and 
            self.shipping_method == self.SHIPPING.STORE):
            
            if hasattr(self, 'order') and self.order:
                notify_Ready_to_pickup.delay(order=self.order)

    def get_price(self, order=None):
        if self.shipping_method == self.SHIPPING.ZASILKOVNA:
            return SiteConfiguration.get_solo().zasilkovna_shipping_price
        elif self.shipping_method == self.SHIPPING.DEUTSCHEPOST:
            return SiteConfiguration.get_solo().deutschepost_shipping_price
        elif self.shipping_method == self.SHIPPING.STORE:
            # Store pickup is always free
            return Decimal('0.0')
        else:
            # Check for free shipping based on order total
            if order is None:
                order = Order.objects.filter(carrier=self).first()
            
            if order and order.total_price >= SiteConfiguration.get_solo().free_shipping_over:
                return Decimal('0.0')
            else:
                return SiteConfiguration.get_solo().default_shipping_price or Decimal('50.0')  # fallback price
        
    
    #tohle bude vyvoláno pomocí admina přes api!!!
    def start_ordering_shipping(self):
        if self.shipping_method == self.SHIPPING.ZASILKOVNA:
            # Uživatel může objednat více zásilek pokud potřeba
            self.zasilkovna.add(ZasilkovnaPacket.objects.create())
            self.returning = False
            self.save()

            notify_zasilkovna_sended.delay(order=self.order)

        elif self.shipping_method == self.SHIPPING.DEUTSCHEPOST:
            # Import here to avoid circular imports
            from thirdparty.deutschepost.models import DeutschePostOrder
            
            # Create new Deutsche Post order and add to carrier (same pattern as zasilkovna)
            dp_order = DeutschePostOrder.objects.create()
            self.deutschepost.add(dp_order)
            self.returning = False
            self.save()
            
            # Order shipping through Deutsche Post API
            dp_order.order_shippment()
            
        elif self.shipping_method == self.SHIPPING.STORE:
            self.state = self.STATE.READY_TO_PICKUP
            self.save()

            notify_Ready_to_pickup.delay(order=self.order)

        else:
            raise ValidationError("Tato metoda dopravy nepodporuje objednání přepravy.")

        
        #... další logika pro jiné způsoby dopravy (do budoucna!)


    def ready_to_pickup(self):
        if self.shipping_method == self.SHIPPING.STORE:
            self.state = self.STATE.READY_TO_PICKUP
            self.save()
        else:
            raise ValidationError("Tato metoda dopravy nepodporuje připravení k vyzvednutí.")

    # def returning_shipping(self, int:id):
    #     self.returning = True

    #     if self.shipping_method == self.SHIPPING.ZASILKOVNA:
    #         #volá se na api Zásilkovny
    #         self.zasilkovna.get(id=id).returning_packet()


# ------------------ PLATEBNÍ MODELY ------------------

class Payment(models.Model):
    class PAYMENT(models.TextChoices):
        SHOP = "shop", "Platba v obchodě"
        STRIPE = "stripe", "Platební Brána"
        CASH_ON_DELIVERY = "cash_on_delivery", "Dobírka"
    payment_method = models.CharField(max_length=30, choices=PAYMENT.choices, default=PAYMENT.SHOP)

    #FIXME: potvrdit že logika platby funguje správně
    #veškera logika a interakce bude na stripu (třeba aktualizovaní objednávky na zaplacenou apod.)
    stripe = models.OneToOneField(
        StripeModel, on_delete=models.CASCADE, null=True, blank=True, related_name="payment"
    )
    payed_at_shop = models.BooleanField(default=False, null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def clean(self):
        """Validate payment and shipping method combinations"""
        # Validate payment method consistency
        if self.payment_method == self.PAYMENT.STRIPE and not self.stripe:
            raise ValidationError("Stripe payment method requires a linked StripeModel instance.")
        
        elif self.payment_method == self.PAYMENT.SHOP and self.stripe:
            raise ValidationError("Shop payment method should not have a linked StripeModel instance.")
        
        # Validate payment and shipping compatibility
        if self.payment_method == self.PAYMENT.SHOP:
            # SHOP payment only works with STORE pickup - customer pays at physical store
            if Order.objects.filter(payment=self).exists():
                order = Order.objects.get(payment=self)
                
                if order.carrier and order.carrier.shipping_method != Carrier.SHIPPING.STORE:
                    raise ValidationError(
                        "Shop payment is only compatible with store pickup. "
                        "For shipping orders, use Stripe or Cash on Delivery payment methods."
                    )
        
        elif self.payment_method == self.PAYMENT.CASH_ON_DELIVERY:
            # Cash on delivery only works with shipping methods (not store pickup)
            if Order.objects.filter(payment=self).exists():
                order = Order.objects.get(payment=self)
                
                if order.carrier and order.carrier.shipping_method == Carrier.SHIPPING.STORE:
                    raise ValidationError(
                        "Cash on delivery is not compatible with store pickup. "
                        "For store pickup, use shop payment method."
                    )
        
        # STRIPE payment works with all shipping methods - no additional validation needed
        
        super().clean()

    def payed_manually(self):
        """Mark payment as completed"""
        if self.payment_method == self.PAYMENT.SHOP:
            self.payed_at_shop = True
            self.save()
        else:
            raise ValidationError("Manuální platba je povolena pouze pro platbu v obchodě.")
            


# ------------------ SLEVOVÉ KÓDY ------------------

class DiscountCode(models.Model):
    code = models.CharField(max_length=50, unique=True)
    description = models.CharField(max_length=255, blank=True)
    
    # sleva v procentech (0–100)
    percent = models.PositiveIntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(100)], 
        help_text="Procento sleva 0-100", 
        null=True, 
        blank=True
    )

    # nebo fixní částka
    amount = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, help_text="Fixed discount amount in site currency")

    valid_from = models.DateTimeField(default=timezone.now)
    valid_to = models.DateTimeField(null=True, blank=True)
    active = models.BooleanField(default=True)

    #max počet použití
    usage_limit = models.PositiveIntegerField(null=True, blank=True)
    used_count = models.PositiveIntegerField(default=0)


    specific_products = models.ManyToManyField(
        Product, blank=True, related_name="discount_codes"
    )
    specific_categories = models.ManyToManyField(
        Category, blank=True, related_name="discount_codes"
    )

    def is_valid(self):
        now = timezone.now()
        if not self.active:
            return False
        
        if self.valid_to and self.valid_to < now:
            return False
        
        if self.usage_limit and self.used_count >= self.usage_limit:
            return False

        return True

    def __str__(self):
        return f"{self.code} ({self.percent}% or {self.amount} CZK)"
    

# ------------------ OBJEDNANÉ POLOŽKY ------------------

class OrderItem(models.Model):
    order = models.ForeignKey(Order, related_name="items", on_delete=models.CASCADE)
    product = models.ForeignKey("commerce.Product", on_delete=models.PROTECT)
    quantity = models.PositiveIntegerField(default=1)

    def get_total_price(self, discounts: list[DiscountCode] = list()):
        """Vrátí celkovou cenu položky po aplikaci relevantních kupónů.

        Logika dle SiteConfiguration:
        - multiplying_coupons=True: procentuální slevy se násobí (sekvenčně)
          P * (1 - p1) -> výsledné * (1 - p2) ...
          jinak se použije pouze nejlepší (nejvyšší procento).
        - addition_of_coupons_amount=True: fixní částky (amount) se sčítají,
          jinak se použije pouze nejvyšší částka.
        - Kombinace: nejprve procentuální část, poté odečtení fixní částky.
        - Sleva se nikdy nesmí dostat pod 0.
        """
        # Use VAT-inclusive price for customer-facing calculations
        base_price = self.product.get_price_with_vat() * self.quantity

        if not discounts or discounts == []:
            return base_price

        config = SiteConfiguration.get_solo()

        #seznám slev
        applicable_percent_discounts: list[int] = []
        applicable_amount_discounts: list[Decimal] = []

        #procházení kupónů a určení, které se aplikují
        for discount in set(discounts):
            if not discount:
                continue

            if not discount.is_valid():
                raise ValueError("Invalid discount code.")

            #defaulting
            applies = False

            # Určení, zda kupon platí pro produkt/kategorii
            # prázdný produkt a kategorie = globální kupon
            if discount.specific_products.exists() or discount.specific_categories.exists():
                if (self.product in discount.specific_products.all() or self.product.category in discount.specific_categories.all()):
                    applies = True

            else:
                applies = True #global

            if not applies:
                continue

            if discount.percent is not None:
                applicable_percent_discounts.append(discount.percent)
            elif discount.amount is not None:
                applicable_amount_discounts.append(discount.amount)

        final_price = base_price

        # Procentuální slevy
        if applicable_percent_discounts:

            if config.multiplying_coupons:
                for pct in applicable_percent_discounts:
                    factor = (Decimal('1') - (Decimal(pct) / Decimal('100')))
                    final_price = final_price * factor
            else:
                best_pct = max(applicable_percent_discounts)
                factor = (Decimal('1') - (Decimal(best_pct) / Decimal('100')))
                final_price = final_price * factor

        # Fixní částky
        if applicable_amount_discounts:
            if config.addition_of_coupons_amount:
                total_amount = sum(applicable_amount_discounts)

            else:
                total_amount = max(applicable_amount_discounts)
                
            final_price = final_price - total_amount

        if final_price < Decimal('0'):
            final_price = Decimal('0')

        return final_price.quantize(Decimal('0.01'))

    def __str__(self):
        return f"{self.product.name} x{self.quantity}"
    
    def save(self, *args, **kwargs):
        if self.pk is None:
            # Check if order already has a processed payment
            if (self.order.payment and 
                self.order.payment.payment_method and 
                self.order.payment.payment_method != Payment.PAYMENT.SHOP):
                raise ValueError("Cannot modify items from order with processed payment method.")
            
            # Validate stock availability
            if self.product.stock < self.quantity:
                raise ValueError(f"Insufficient stock for product {self.product.name}. Available: {self.product.stock}")

            # Reduce stock
            self.product.stock -= self.quantity
            self.product.save(update_fields=["stock"])

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

class Refund(models.Model):
    order = models.ForeignKey(Order, related_name="refunds", on_delete=models.CASCADE)

    class Reason(models.TextChoices):
        RETUNING_PERIOD = "retuning_before_fourteen_day_period", "Vrácení před uplynutím 14-ti denní lhůty"
        DAMAGED_PRODUCT = "damaged_product", "Poškozený produkt"
        WRONG_ITEM = "wrong_item", "Špatná položka"
        OTHER = "other", "Jiný důvod"
    reason_choice = models.CharField(max_length=40, choices=Reason.choices)

    reason_text = models.TextField(blank=True)

    verified = models.BooleanField(default=False)

    created_at = models.DateTimeField(auto_now_add=True)

    #VRACENÍ ZÁSILKY, LOGIKA (DISABLED FOR NOW)
    # def save(self, *args, **kwargs):
    #     # Automaticky aktualizovat stav objednávky na "vráceno"
    #     if self.pk is None:
    #         self.order.status = Order.Status.REFUNDING
    #         self.order.save(update_fields=["status", "updated_at"])

    #         shipping_method = self.order.carrier.shipping_method

    #         if shipping_method == Carrier.SHIPPING.ZASILKOVNA:

    #             carrier = self.order.carrier;

    #             # poslední odeslána/vytvořená zásilka                
    #             # Iniciovat vrácení přes Zásilkovnu
    #             carrier.zasilkovna.latest('created_at').returning_packet()
    #             carrier.save()
                
    #         else:
    #             # Logika pro jiné způsoby dopravy
    #             pass

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

    def save(self, *args, **kwargs):
        # Automaticky aktualizovat stav objednávky na "vráceno"
        if self.pk is None:
            if self.order.status != Order.OrderStatus.REFUNDING:
                self.order.status = Order.OrderStatus.REFUNDING
                self.order.save(update_fields=["status", "updated_at"])

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

    def refund_completed(self):
        # Aktualizovat stav objednávky na "vráceno"
        if self.order.payment and self.order.payment.payment_method == Payment.PAYMENT.STRIPE:
            self.order.payment.stripe.refund() # Vrácení pěnez přes stripe

        self.order.status = Order.OrderStatus.REFUNDED
        self.order.save(update_fields=["status", "updated_at"])

        
        notify_refund_accepted.delay(order=self.order)


    def generate_refund_pdf_for_customer(self):
        """Vygeneruje PDF formulář k vrácení zboží pro zákazníka.

        Šablona refund/customer_in_package_returning_form.html očekává:
        - order: objekt objednávky
        - items: seznam položek (dict) s klíči product_name, sku, quantity, variant, options, reason
        - return_reason: textový důvod vrácení (kombinace reason_text / reason_choice)

        Návratová hodnota: bytes (PDF obsah). Uložení necháváme na volající logice.
        """
        order = self.order

        # Připravíme položky pro šablonu (důvody per položku zatím None – lze rozšířit)
        prepared_items: list[dict] = []
        for item in order.items.select_related('product'):
            prepared_items.append({
                "product_name": getattr(item.product, "name", "Item"),
                "name": getattr(item.product, "name", "Item"),  # fallbacky pro různé názvy v šabloně
                "sku": getattr(item.product, "code", None),
                "quantity": item.quantity,
                "variant": None,   # lze doplnit pokud existují varianty
                "options": None,   # lze doplnit pokud existují volby
                "reason": None,    # per-item reason (zatím nepodporováno)
            })

        return_reason = self.reason_text or self.get_reason_choice_display()

        context = {
            "order": order,
            "items": prepared_items,
            "return_reason": return_reason,
        }

        html_string = render_to_string("refund/customer_in_package_returning_form.html", context)
        
        # Import WeasyPrint lazily to avoid startup failures when system
        # libraries (Pango/GObject) are not present on Windows.
        if HTML is None:
            raise RuntimeError(
                "WeasyPrint is not available. Install its system dependencies (Pango/GTK) or run the backend in Docker."
            )
        
        pdf_bytes = HTML(string=html_string).write_pdf()
        return pdf_bytes


class Invoice(models.Model):
    invoice_number = models.CharField(max_length=50, unique=True)

    issued_at = models.DateTimeField(auto_now_add=True)
    due_date = models.DateTimeField()

    pdf_file = models.FileField(upload_to='invoices/')

    def __str__(self):
        return f"Invoice {self.invoice_number} for Order {self.order.id}"
    
    def generate_invoice_pdf(self):
        order = Order.objects.get(invoice=self)
        # Render HTML
        html_string = render_to_string("invoice/Order.html", {"invoice": self, "order": order})
        
        # Import WeasyPrint lazily to avoid startup failures when system
        # libraries (Pango/GObject) are not present on Windows.
        if HTML is None:
            raise RuntimeError(
                "WeasyPrint is not available. Install its system dependencies (Pango/GTK) or run the backend in Docker."
            )

        pdf_bytes = HTML(string=html_string).write_pdf()

        # Save directly to FileField
        self.pdf_file.save(f"{self.invoice_number}.pdf", ContentFile(pdf_bytes))
        self.save()


class Review(models.Model):
    product = models.ForeignKey(Product, related_name="reviews", on_delete=models.CASCADE)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="reviews"
    )
    
    rating = models.PositiveIntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(5)]
    )
    comment = models.TextField(blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ('product', 'user')  # Prevent multiple reviews per user per product
        indexes = [
            models.Index(fields=['product', 'rating']),
            models.Index(fields=['created_at']),
        ]

    def clean(self):
        """Validate that user hasn't already reviewed this product"""
        if self.pk is None:  # Only for new reviews
            if Review.objects.filter(product=self.product, user=self.user).exists():
                raise ValidationError("User has already reviewed this product.")

    def __str__(self):
        return f"Review for {self.product.name} by {self.user.username}"


# ------------------ SHOPPING CART ------------------

class Cart(models.Model):
    """Shopping cart for both authenticated and anonymous users"""
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL, 
        on_delete=models.CASCADE, 
        null=True, 
        blank=True,
        related_name="cart"
    )
    session_key = models.CharField(
        max_length=40, 
        null=True, 
        blank=True,
        help_text="Session key for anonymous users"
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "Cart"
        verbose_name_plural = "Carts"

    def __str__(self):
        if self.user:
            return f"Cart for {self.user.email}"
        return f"Anonymous cart ({self.session_key})"

    def get_total(self):
        """Calculate total price of all items in cart including VAT"""
        total = Decimal('0.0')
        for item in self.items.all():
            total += item.get_subtotal()
        return total

    def get_items_count(self):
        """Get total number of items in cart"""
        return sum(item.quantity for item in self.items.all())


class CartItem(models.Model):
    """Individual items in a shopping cart"""
    cart = models.ForeignKey(Cart, related_name='items', on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField(default=1)
    added_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = "Cart Item"
        verbose_name_plural = "Cart Items"
        unique_together = ('cart', 'product')  # Prevent duplicate products in same cart

    def __str__(self):
        return f"{self.quantity}x {self.product.name} in cart"

    def get_subtotal(self):
        """Calculate subtotal for this cart item including VAT"""
        return self.product.get_price_with_vat() * self.quantity

    def clean(self):
        """Validate that product has enough stock"""
        if self.product.stock < self.quantity:
            raise ValidationError(f"Not enough stock for {self.product.name}. Available: {self.product.stock}")

    def save(self, *args, **kwargs):
        self.clean()
        super().save(*args, **kwargs)


# ------------------ WISHLIST ------------------

class Wishlist(models.Model):
    """User's wishlist for saving favorite products"""
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="wishlist"
    )
    products = models.ManyToManyField(
        Product,
        blank=True,
        related_name="wishlisted_by",
        help_text="Products saved by the user"
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "Wishlist"
        verbose_name_plural = "Wishlists"

    def __str__(self):
        return f"Wishlist for {self.user.email}"

    def add_product(self, product):
        """Add a product to wishlist"""
        self.products.add(product)

    def remove_product(self, product):
        """Remove a product from wishlist"""
        self.products.remove(product)

    def has_product(self, product):
        """Check if product is in wishlist"""
        return self.products.filter(pk=product.pk).exists()

    def get_products_count(self):
        """Get count of products in wishlist"""
        return self.products.count()