from django.db import models
from apps.core.models import BaseModel


class Country(BaseModel):
    name = models.CharField(max_length=200)
    code = models.CharField(max_length=50, unique=True, db_index=True)

    def __str__(self):
        return self.name
    
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['code'], name='unique_country_code'),
        ]


class State(BaseModel):
    name = models.CharField(max_length=200, db_index=True)
    country = models.ForeignKey(Country, related_name='states', on_delete=models.CASCADE, db_index=True)

    def __str__(self):
        return f"{self.name} ({self.country.code})"
    
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['name', 'country'], name='unique_state_per_country'),
        ]


class District(BaseModel):
    name = models.CharField(max_length=200, db_index=True)
    state = models.ForeignKey(State, related_name='districts', on_delete=models.CASCADE, db_index=True)

    def __str__(self):
        return f"{self.name} ({self.state.name})"
    
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['name', 'state'], name='unique_district_per_state'),
        ]


class Mandal(BaseModel):
    name = models.CharField(max_length=200, db_index=True)
    district = models.ForeignKey(District, related_name='mandals', on_delete=models.CASCADE, db_index=True)

    def __str__(self):
        return f"{self.name} ({self.district.name})"

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['name', 'district'], name='unique_mandal_per_district'),
        ]


class Pincode(BaseModel):
    code = models.CharField(max_length=20, db_index=True)
    country = models.ForeignKey(Country, related_name='pincodes', on_delete=models.SET_NULL, db_index=True, null=True, blank=True)
    state = models.ForeignKey(State, related_name='pincodes', on_delete=models.SET_NULL, db_index=True, null=True, blank=True)
    district = models.ForeignKey(District, related_name='pincodes', on_delete=models.SET_NULL, db_index=True, null=True, blank=True)
    location = models.CharField(max_length=255, db_index=True, null=True, blank=True, default='')

    class Meta:
        indexes = [models.Index(fields=['code']), models.Index(fields=['location'])]
        constraints = [
            models.UniqueConstraint(fields=['code', 'location'], name='unique_pincode_location'),
        ]

    def __str__(self):
        return f"{self.code} - {self.location}"


class FranchiseStore(BaseModel):
    code = models.CharField(max_length=30, unique=True, db_index=True)
    name = models.CharField(max_length=200, db_index=True)
    country = models.ForeignKey(Country, related_name='franchise_stores', on_delete=models.SET_NULL, null=True, blank=True)
    state = models.ForeignKey(State, related_name='franchise_stores', on_delete=models.SET_NULL, null=True, blank=True)
    district = models.ForeignKey(District, related_name='franchise_stores', on_delete=models.SET_NULL, null=True, blank=True)
    mandal = models.ForeignKey(Mandal, related_name='franchise_stores', on_delete=models.SET_NULL, null=True, blank=True)
    pincode = models.ForeignKey(Pincode, related_name='franchise_stores', on_delete=models.SET_NULL, null=True, blank=True)
    address_line1 = models.CharField(max_length=255, blank=True, default='')
    address_line2 = models.CharField(max_length=255, blank=True, default='')
    contact_name = models.CharField(max_length=200, blank=True, default='')
    contact_mobile = models.CharField(max_length=20, blank=True, default='')

    class Meta:
        ordering = ['name']

    def __str__(self):
        return f"{self.name} ({self.code})"


class FranchiseAssignment(BaseModel):
    class TerritoryLevel(models.TextChoices):
        STATE = 'STATE', 'State'
        DISTRICT = 'DISTRICT', 'District'
        MANDAL = 'MANDAL', 'Mandal'

    user = models.ForeignKey('accounts.User', on_delete=models.CASCADE, related_name='franchise_assignments')
    store = models.ForeignKey(FranchiseStore, on_delete=models.SET_NULL, null=True, blank=True, related_name='assignments')
    territory_level = models.CharField(max_length=16, choices=TerritoryLevel.choices, db_index=True)
    state = models.ForeignKey(State, on_delete=models.SET_NULL, null=True, blank=True, related_name='franchise_assignments')
    district = models.ForeignKey(District, on_delete=models.SET_NULL, null=True, blank=True, related_name='franchise_assignments')
    mandal = models.ForeignKey(Mandal, on_delete=models.SET_NULL, null=True, blank=True, related_name='franchise_assignments')
    role_title = models.CharField(max_length=120, blank=True, default='Franchise Head')
    performance_note = models.CharField(max_length=500, blank=True, default='')
    assignment_reason = models.CharField(max_length=500, blank=True, default='')
    effective_from = models.DateTimeField(auto_now_add=True)
    effective_to = models.DateTimeField(null=True, blank=True)
    assigned_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_franchise_records')
    removed_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='removed_franchise_records')

    class Meta:
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['territory_level', 'is_active']),
            models.Index(fields=['user', 'is_active']),
            models.Index(fields=['state', 'district', 'mandal']),
        ]

    def __str__(self):
        return f"{self.territory_level} -> {self.user_id}"


class StoreTransferLedger(BaseModel):
    class TransferType(models.TextChoices):
        COMPANY_TO_STORE = 'COMPANY_TO_STORE', 'Company To Store'
        STORE_TO_CUSTOMER = 'STORE_TO_CUSTOMER', 'Store To Customer'
        CUSTOMER_PICKUP = 'CUSTOMER_PICKUP', 'Customer Pickup'

    class TransferStatus(models.TextChoices):
        INITIATED = 'INITIATED', 'Initiated'
        IN_TRANSIT = 'IN_TRANSIT', 'In Transit'
        RECEIVED = 'RECEIVED', 'Received'
        COMPLETED = 'COMPLETED', 'Completed'
        CANCELLED = 'CANCELLED', 'Cancelled'

    order = models.ForeignKey('orders.Order', on_delete=models.SET_NULL, null=True, blank=True, related_name='store_transfers')
    store = models.ForeignKey(FranchiseStore, on_delete=models.SET_NULL, null=True, blank=True, related_name='transfer_ledgers')
    transfer_type = models.CharField(max_length=24, choices=TransferType.choices, db_index=True)
    transfer_status = models.CharField(max_length=16, choices=TransferStatus.choices, default=TransferStatus.INITIATED, db_index=True)
    quantity = models.PositiveIntegerField(default=1)
    remarks = models.CharField(max_length=500, blank=True, default='')
    performed_by = models.ForeignKey('accounts.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='performed_store_transfers')

    class Meta:
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['store', 'created_at']),
            models.Index(fields=['order', 'created_at']),
            models.Index(fields=['transfer_type', 'transfer_status']),
        ]

    def __str__(self):
        return f"{self.transfer_type} ({self.transfer_status})"


# =============================================================================
# MARKETPLACE FRANCHISE EXTENSIONS
# These models extend FranchiseStore for retail marketplace operations.
# They are owned by the geo app so FranchiseStore remains the single authority.
# =============================================================================

class FranchisePincodeCoverage(BaseModel):
    """
    Maps delivery pincodes to franchise stores for marketplace fulfillment routing.
    One row per (store, pincode). Lookup: pincode → eligible stores.
    """
    store = models.ForeignKey(
        FranchiseStore, on_delete=models.CASCADE,
        related_name='pincode_coverage',
    )
    pincode = models.ForeignKey(
        Pincode, on_delete=models.SET_NULL,
        null=True, blank=True,
        related_name='franchise_coverage',
        help_text="FK to canonical Pincode record (optional; pincode_code is the fast-lookup field)",
    )
    pincode_code = models.CharField(
        max_length=20, db_index=True,
        help_text="Denormalised pincode string for fast O(1) lookup without JOIN",
    )
    city = models.CharField(max_length=100, blank=True, default='')
    state = models.CharField(max_length=100, blank=True, default='')
    is_serviceable = models.BooleanField(default=True)
    assigned_date = models.DateField(auto_now_add=True)

    class Meta:
        unique_together = ('store', 'pincode_code')
        indexes = [
            models.Index(fields=['pincode_code', 'is_serviceable']),
            models.Index(fields=['store', 'is_serviceable']),
        ]

    def __str__(self):
        return f"{self.store.name} → {self.pincode_code}"


class FranchiseStoreInventory(BaseModel):
    """
    Per-product inventory held at a franchise store for marketplace order fulfillment.
    SINGLE SOURCE OF TRUTH for franchise-side stock.
    Use SELECT FOR UPDATE when reading stock for order reservation.
    """
    store = models.ForeignKey(
        FranchiseStore, on_delete=models.CASCADE,
        related_name='marketplace_inventory',
    )
    product = models.ForeignKey(
        'products.Product', on_delete=models.CASCADE,
        related_name='franchise_inventories',
    )
    stock = models.PositiveIntegerField(default=0)
    reserved_stock = models.PositiveIntegerField(default=0)
    reorder_point = models.PositiveIntegerField(
        default=5,
        help_text="Alert threshold for low-stock notifications",
    )
    max_stock_level = models.PositiveIntegerField(
        default=100,
        help_text="Maximum stock ceiling for auto-reorder calculations",
    )
    auto_reorder = models.BooleanField(
        default=False,
        help_text="Automatically create a replenishment request when below reorder_point",
    )
    last_restocked = models.DateTimeField(null=True, blank=True)

    class Meta:
        unique_together = ('store', 'product')
        indexes = [
            models.Index(fields=['store', 'product']),
            models.Index(fields=['product', 'store']),
        ]

    @property
    def available_stock(self):
        return max(0, self.stock - self.reserved_stock)

    @property
    def is_low_stock(self):
        return self.available_stock <= self.reorder_point

    def __str__(self):
        return f"{self.store.name} | {self.product_id} | avail={self.available_stock}"


class FranchiseStoreEarning(BaseModel):
    """
    Commission earning record for a franchise store from a marketplace order.
    Created by OrderService after order is confirmed; credited on delivery.
    Business logic lives in FranchiseEarningService — not in save().
    """
    EARNING_TYPE_CHOICES = [
        ('FULFILLMENT', 'Order Fulfillment'),
        ('DELIVERY', 'Local Delivery'),
        ('PICKUP', 'Customer Pickup'),
    ]
    STATUS_CHOICES = [
        ('PENDING', 'Pending'),
        ('CREDITED', 'Credited'),
        ('CANCELLED', 'Cancelled'),
    ]

    store = models.ForeignKey(
        FranchiseStore, on_delete=models.CASCADE,
        related_name='marketplace_earnings',
    )
    order = models.ForeignKey(
        'orders.Order', on_delete=models.CASCADE,
        related_name='franchise_earnings',
    )
    order_amount = models.DecimalField(max_digits=12, decimal_places=2)
    commission_percentage = models.DecimalField(max_digits=5, decimal_places=2)
    commission_amount = models.DecimalField(max_digits=12, decimal_places=2)
    earning_type = models.CharField(
        max_length=20, choices=EARNING_TYPE_CHOICES, default='FULFILLMENT',
    )
    status = models.CharField(
        max_length=20, choices=STATUS_CHOICES, default='PENDING', db_index=True,
    )
    credited_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        indexes = [
            models.Index(fields=['store', 'status']),
            models.Index(fields=['order']),
            models.Index(fields=['status', 'created_at']),
        ]

    def __str__(self):
        return f"{self.store.name} | ₹{self.commission_amount} | {self.status}"
