# sales/models.py
import random
import string
from django.db import models
from inventory.models import Product
from core.models import PromoCode
from decimal import Decimal
from django.db.models import Sum, Count, Q
from companies.models import Company


def _random_suffix(k=5):
    """Return k random uppercase alphanumeric characters."""
    return ''.join(random.choices(string.ascii_uppercase + string.digits, k=k))


class POSOrder(models.Model):
    # Payment methods – 'credit' removed; use payment_status instead
    PAYMENT_METHODS = [
        ('pos', 'POS/ATM Card'),
        ('transfer', 'Bank Transfer'),
        ('cash', 'Cash'),
        ('mobile_money', 'Mobile Money'),
        ('split', 'Split Payment'),
    ]

    ORDER_STATUS = [
        ('pending', 'Pending'),
        ('completed', 'Completed'),
        ('cancelled', 'Cancelled'),
    ]

    PAYMENT_STATUS_CHOICES = [
        ('full', 'Paid in Full'),
        ('partial', 'Partial Payment'),
        ('credit', 'Credit / Customer Owing'),
    ]

    order_number = models.CharField(max_length=60, unique=True)

    company = models.ForeignKey(
        Company, on_delete=models.CASCADE, null=True, blank=True,
        related_name='orders', db_index=True,
    )

    # Deduplication key for offline sync
    client_id = models.CharField(
        max_length=64, blank=True, db_index=True,
        help_text="UUID generated by the client to prevent duplicate offline orders."
    )

    # Money fields
    total_amount   = models.DecimalField(max_digits=10, decimal_places=2)
    discount_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    tax_amount     = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    final_amount   = models.DecimalField(max_digits=10, decimal_places=2)

    payment_method = models.CharField(max_length=20, choices=PAYMENT_METHODS, default='cash')
    status = models.CharField(max_length=15, choices=ORDER_STATUS, default='pending')

    # Split payment fields
    is_split_payment = models.BooleanField(default=False)
    split_method_1 = models.CharField(max_length=20, blank=True)
    split_method_2 = models.CharField(max_length=20, blank=True)

    # Credit tracking
    payment_status = models.CharField(
        max_length=10,
        choices=PAYMENT_STATUS_CHOICES,
        default='full',
        help_text="Whether the order is fully paid or has an outstanding balance."
    )
    amount_paid = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    balance_amount = models.DecimalField(
        max_digits=10, decimal_places=2, default=0,
        help_text="Outstanding balance = final_amount - amount_paid."
    )

    # Customer info — name/phone kept for walk-in / legacy orders
    customer_name  = models.CharField(max_length=100, blank=True)
    customer_phone = models.CharField(max_length=15, blank=True)

    # FK to registered Customer (optional; set when customer selected at POS)
    customer = models.ForeignKey(
        'sales.Customer',
        on_delete=models.SET_NULL,
        null=True, blank=True,
        related_name='orders',
    )

    # Branch
    branch = models.ForeignKey(
        'core.Branch',
        on_delete=models.SET_NULL,
        null=True, blank=True,
        related_name='pos_orders'
    )

    # Staff
    cashier    = models.CharField(max_length=100, db_index=True)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    updated_at = models.DateTimeField(auto_now=True)

    # Order origin — POS terminal, Online Storefront, or Table QR self-checkout
    SOURCE_CHOICES = [
        ('pos',    'POS Terminal'),
        ('online', 'Online Store'),
        ('table',  'Table QR Checkout'),
    ]
    source = models.CharField(max_length=10, choices=SOURCE_CHOICES, default='pos', db_index=True)

    def clear_credit(self, amount=None):
        """
        Clears customer credit. Accepts partial or full amount.
        """
        if amount is None:
            amount = self.balance_amount
        if not isinstance(amount, Decimal):
            amount = Decimal(str(amount))
        if amount <= 0:
            return False

        self.amount_paid   = (self.amount_paid or Decimal("0")) + amount
        self.balance_amount = self.final_amount - self.amount_paid

        if self.balance_amount <= 0:
            self.balance_amount = Decimal("0")
            self.payment_status = "full"
        elif self.amount_paid > 0:
            self.payment_status = "partial"
        else:
            self.payment_status = "credit"

        self.save()
        return True

    @property
    def is_fully_paid(self):
        return self.balance_amount <= 0

    @property
    def split_payments(self):
        """Returns completed PaymentTransaction records for split payment display."""
        return self.payments.filter(status='completed')

    def __str__(self):
        return self.order_number


class POSOrderItem(models.Model):
    order    = models.ForeignKey(POSOrder, on_delete=models.CASCADE, related_name='items')
    # Product protected from deletion; name snapshot preserved
    product  = models.ForeignKey(
        Product,
        on_delete=models.SET_NULL,
        null=True, blank=True,
    )
    # Which variant was sold (null = base product, no variant)
    variant  = models.ForeignKey(
        'inventory.ProductVariant',
        on_delete=models.SET_NULL,
        null=True, blank=True,
        related_name='order_items',
        help_text='Specific product variant sold (null = base product)',
    )
    product_name = models.CharField(
        max_length=200, blank=True,
        help_text="Snapshot of product/variant name at time of sale."
    )
    quantity   = models.DecimalField(
        max_digits=10, decimal_places=4,
        help_text="Supports fractional quantities e.g. 0.5 for half a pack."
    )
    unit_price = models.DecimalField(max_digits=10, decimal_places=2)
    total_price = models.DecimalField(max_digits=10, decimal_places=2)

    class Meta:
        verbose_name = 'POS Order Item'
        verbose_name_plural = 'POS Order Items'

    def save(self, *args, **kwargs):
        # Auto-snapshot product name (include variant label if present)
        if not self.product_name:
            if self.variant:
                self.product_name = f"{self.variant.product.name} ({self.variant.display_label})"
            elif self.product:
                self.product_name = self.product.name
        super().save(*args, **kwargs)

    def __str__(self):
        name = self.product_name or (self.product.name if self.product else 'Unknown')
        return f"{self.quantity}x {name}"



class POSReceiptSettings(models.Model):
    order = models.OneToOneField(POSOrder, on_delete=models.CASCADE, related_name='receipt_settings')
    show_product_details = models.BooleanField(default=True)
    show_customer_info   = models.BooleanField(default=True)
    show_payment_info    = models.BooleanField(default=True)
    custom_message       = models.TextField(blank=True)
    created_at           = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Receipt settings for {self.order.order_number}"


class PaymentTransaction(models.Model):
    PAYMENT_METHODS = [
        ('pos', 'POS/ATM Card'),
        ('transfer', 'Bank Transfer'),
        ('cash', 'Cash'),
        ('mobile_money', 'Mobile Money'),
        ('paystack', 'Paystack'),
        ('flutterwave', 'Flutterwave'),
    ]

    TRANSACTION_STATUS = [
        ('pending', 'Pending'),
        ('completed', 'Completed'),
        ('failed', 'Failed'),
        ('cancelled', 'Cancelled'),
    ]

    order          = models.ForeignKey(POSOrder, on_delete=models.CASCADE, related_name='payments')
    payment_method = models.CharField(max_length=20, choices=PAYMENT_METHODS)
    amount         = models.DecimalField(max_digits=10, decimal_places=2)
    reference_number = models.CharField(max_length=100, blank=True)
    transaction_id   = models.CharField(max_length=100, blank=True)
    status           = models.CharField(max_length=15, choices=TRANSACTION_STATUS, default='pending')
    gateway_response = models.JSONField(default=dict, blank=True)
    created_at       = models.DateTimeField(auto_now_add=True)
    updated_at       = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = 'Payment Transaction'
        verbose_name_plural = 'Payment Transactions'
        ordering = ['-created_at']

    def __str__(self):
        return f"{self.payment_method} - {self.amount} - {self.status}"


# ─── DailySalesSummary (was: SaleSummary merged into this) ────────────────────
class DailySalesSummary(models.Model):
    """Single pre-computed daily summary. SaleSummary was removed (duplicate)."""
    company = models.ForeignKey(
        Company, on_delete=models.CASCADE, null=True, blank=True,
        related_name='daily_summaries', db_index=True,
    )
    date              = models.DateField()  # unique_together below
    total_revenue     = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    total_transactions = models.PositiveIntegerField(default=0)

    cash_revenue      = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    cash_transactions = models.PositiveIntegerField(default=0)

    transfer_revenue      = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    transfer_transactions = models.PositiveIntegerField(default=0)

    pos_revenue      = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    pos_transactions = models.PositiveIntegerField(default=0)

    mobile_money_revenue      = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    mobile_money_transactions = models.PositiveIntegerField(default=0)

    top_selling_items  = models.JSONField(default=list, blank=True)
    total_profit       = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    credit_outstanding = models.DecimalField(max_digits=12, decimal_places=2, default=0)

    # Cached P&L columns (set by generate_summary_from_queryset)
    total_cost    = models.DecimalField(max_digits=12, decimal_places=2, default=0)

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

    class Meta:
        verbose_name = 'Daily Sales Summary'
        verbose_name_plural = 'Daily Sales Summaries'
        ordering = ['-date']
        unique_together = [['company', 'date']]  # one summary per company per day

    def __str__(self):
        return f"Sales Summary for {self.date}"

    @classmethod
    def generate_summary_from_queryset(cls, date, orders):
        """Generate summary ONLY from branch-limited queryset."""
        summary_data = {
            'date': date,
            'total_revenue': orders.aggregate(total=Sum('final_amount'))['total'] or Decimal("0"),
            'total_transactions': orders.count(),
        }

        for method in ['cash', 'transfer', 'pos', 'mobile_money']:
            m_orders = orders.filter(payment_method=method)
            summary_data[f'{method}_revenue']      = m_orders.aggregate(total=Sum('final_amount'))['total'] or 0
            summary_data[f'{method}_transactions'] = m_orders.count()

        total_profit = Decimal("0")
        total_cost   = Decimal("0")
        items = POSOrderItem.objects.filter(order__in=orders).select_related("product")
        for item in items:
            selling = item.unit_price or 0
            cost    = (item.product.cost_price if item.product else None) or 0
            profit  = (Decimal(str(selling)) - Decimal(str(cost))) * item.quantity
            total_profit += profit
            total_cost   += Decimal(str(cost)) * item.quantity

        summary_data['total_profit'] = total_profit
        summary_data['total_cost']   = total_cost
        summary_data['credit_outstanding'] = (
            orders.filter(balance_amount__gt=0).aggregate(total=Sum('balance_amount'))['total'] or 0
        )

        summary_data['company'] = orders.first().company if orders.exists() else None
        
        summary, _ = cls.objects.update_or_create(
            company=summary_data['company'],
            date=date,
            defaults=summary_data
        )
        return summary


class UnusualTransaction(models.Model):
    company     = models.ForeignKey(
        Company, on_delete=models.CASCADE, null=True, blank=True,
        related_name='unusual_transactions', db_index=True,
    )
    order       = models.ForeignKey(POSOrder, on_delete=models.CASCADE)
    reason      = models.CharField(max_length=200)
    flagged_by  = models.CharField(max_length=100)
    flagged_at  = models.DateTimeField(auto_now_add=True)
    resolved    = models.BooleanField(default=False)
    resolved_by = models.CharField(max_length=100, blank=True)
    resolved_at = models.DateTimeField(blank=True, null=True)

    class Meta:
        verbose_name = 'Unusual Transaction'
        verbose_name_plural = 'Unusual Transactions'
        ordering = ['-flagged_at']

    def __str__(self):
        return f"Unusual: {self.order.order_number} - {self.reason}"


# ─────────────────────────────────────────────────────────────
# CASH RECONCILIATION (new — end-of-day)
# ─────────────────────────────────────────────────────────────
class CashReconciliation(models.Model):
    company  = models.ForeignKey(
        Company, on_delete=models.CASCADE, null=True, blank=True,
        related_name='cash_reconciliations', db_index=True,
    )
    date     = models.DateField()  # unique_together below
    expected = models.DecimalField(max_digits=12, decimal_places=2, default=0,
                                   help_text="Sum of cash orders for this day.")
    actual   = models.DecimalField(max_digits=12, decimal_places=2, default=0,
                                   help_text="Physical cash count entered by cashier.")
    variance = models.DecimalField(max_digits=12, decimal_places=2, default=0,
                                   help_text="actual - expected (positive = surplus, negative = shortage).")
    cashier  = models.CharField(max_length=100, blank=True)
    notes    = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = 'Cash Reconciliation'
        verbose_name_plural = 'Cash Reconciliations'
        ordering = ['-date']
        unique_together = [['company', 'date']]  # one reconciliation per company per day

    def save(self, *args, **kwargs):
        self.variance = self.actual - self.expected
        super().save(*args, **kwargs)

    def __str__(self):
        return f"Reconciliation {self.date} — variance {self.variance}"


# ─────────────────────────────────────────────────────────────
# CUSTOMER MODEL
# ─────────────────────────────────────────────────────────────
class Customer(models.Model):
    """Persistent customer record."""
    company    = models.ForeignKey(
        Company, on_delete=models.CASCADE, null=True, blank=True,
        related_name='customers', db_index=True,
    )
    name       = models.CharField(max_length=150)
    phone      = models.CharField(max_length=20, blank=True)
    email      = models.EmailField(blank=True)
    address    = models.TextField(blank=True)
    notes      = models.TextField(blank=True)
    created_by = models.CharField(max_length=100, blank=True)

    # Assign a custom price list. Auto-applied at POS when this customer is selected.
    price_list = models.ForeignKey(
        'sales.PriceList',
        on_delete=models.SET_NULL,
        null=True, blank=True,
        related_name='customers',
        help_text='When set, prices from this list override retail prices at POS'
    )

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

    class Meta:
        verbose_name = 'Customer'
        verbose_name_plural = 'Customers'
        ordering = ['name']

    def __str__(self):
        return f"{self.name} ({self.phone})" if self.phone else self.name

    @property
    def total_orders(self):
        return self.orders.count()

    @property
    def total_spent(self):
        result = self.orders.filter(status='completed').aggregate(total=Sum('final_amount'))
        return result['total'] or 0

    @property
    def outstanding_balance(self):
        result = self.orders.filter(balance_amount__gt=0).aggregate(total=Sum('balance_amount'))
        return result['total'] or 0


# ─────────────────────────────────────────────────────────────
# REFUND MODEL (order reversal / cancellation)
# ─────────────────────────────────────────────────────────────
class Refund(models.Model):
    """Records a full reversal/refund of a completed order."""
    company = models.ForeignKey(
        Company,
        on_delete=models.CASCADE,
        null=True, blank=True,
        related_name='refunds',
        db_index=True,
    )
    original_order = models.OneToOneField(
        POSOrder,
        on_delete=models.CASCADE,
        related_name='refund',
    )
    refunded_by = models.CharField(max_length=150)
    reason = models.TextField(blank=True)
    refund_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    stock_restored = models.BooleanField(default=False)
    credit_adjusted = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = 'Refund'
        verbose_name_plural = 'Refunds'
        ordering = ['-created_at']

    def __str__(self):
        return f"Refund of {self.original_order.order_number} by {self.refunded_by}"


# -------------------------------------------------------------
# CUSTOMER PRICE LISTS  (Tiered / custom pricing per customer)
# -------------------------------------------------------------

class PriceList(models.Model):
    """
    A named set of price overrides assigned to specific customers.
    Examples: 'Wholesale Rate', 'VIP Discount', 'Staff Pricing'.
    When a customer with a price_list is selected at POS, their
    prices automatically replace the standard retail price.
    """
    company    = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='price_lists', db_index=True)
    name       = models.CharField(max_length=100, help_text='e.g. Wholesale Rate, VIP, Staff')
    is_default = models.BooleanField(default=False, help_text='Apply to all unassigned customers')
    is_active  = models.BooleanField(default=True)
    notes      = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name        = 'Price List'
        verbose_name_plural = 'Price Lists'
        unique_together     = [['company', 'name']]
        ordering            = ['name']

    def __str__(self):
        return f"{self.name} ({'Default' if self.is_default else 'Custom'})"


class PriceListItem(models.Model):
    """
    A single product/variant override within a PriceList.
    The override_price replaces the standard retail price at checkout.
    """
    price_list     = models.ForeignKey(PriceList, on_delete=models.CASCADE, related_name='items')
    product        = models.ForeignKey('inventory.Product', on_delete=models.CASCADE)
    variant        = models.ForeignKey(
        'inventory.ProductVariant', on_delete=models.CASCADE,
        null=True, blank=True,
        help_text='Leave blank to apply override to all variants of this product'
    )
    override_price = models.DecimalField(max_digits=10, decimal_places=2)

    class Meta:
        verbose_name        = 'Price List Item'
        verbose_name_plural = 'Price List Items'
        unique_together     = [['price_list', 'product', 'variant']]

    def __str__(self):
        v_label = f" [{self.variant.display_label}]" if self.variant else ""
        return f"{self.product.name}{v_label} -> {self.override_price} ({self.price_list.name})"
