import string
import uuid

from django.conf import settings
from django.db import models
from django.utils.crypto import get_random_string


# ---------------------------------------------------------------------------
# Access Control
# ---------------------------------------------------------------------------


class AccessPoint(models.Model):
    class AccessType(models.TextChoices):
        MAIN_ENTRANCE = "main_entrance", "Main Entrance"
        GYM_FLOOR = "gym_floor", "Gym Floor"
        POOL = "pool", "Pool"
        LOCKER_ROOM = "locker_room", "Locker Room"
        STUDIO = "studio", "Studio"
        VIP = "vip", "VIP"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=150)
    location = models.ForeignKey(
        "core.Location",
        on_delete=models.CASCADE,
        related_name="access_points",
    )
    access_type = models.CharField(
        max_length=20,
        choices=AccessType.choices,
    )
    hardware_id = models.CharField(max_length=255, blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["name"]
        verbose_name = "Access Point"
        verbose_name_plural = "Access Points"

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


class AccessLog(models.Model):
    class Direction(models.TextChoices):
        ENTRY = "entry", "Entry"
        EXIT = "exit", "Exit"

    class Method(models.TextChoices):
        QR_CODE = "qr_code", "QR Code"
        RFID = "rfid", "RFID"
        BIOMETRIC = "biometric", "Biometric"
        MANUAL = "manual", "Manual"
        PIN = "pin", "PIN"

    class PersonType(models.TextChoices):
        MEMBER = "member", "Member"
        VISITOR = "visitor", "Visitor"
        GUEST = "guest", "Guest"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    member = models.ForeignKey(
        "members.MemberProfile",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="access_logs",
    )
    visitor = models.ForeignKey(
        "operations.Visitor",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="access_logs",
    )
    person_type = models.CharField(
        max_length=10,
        choices=PersonType.choices,
        default=PersonType.MEMBER,
    )
    access_point = models.ForeignKey(
        AccessPoint,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="access_logs",
    )
    direction = models.CharField(
        max_length=10,
        choices=Direction.choices,
    )
    method = models.CharField(
        max_length=20,
        choices=Method.choices,
    )
    class_session = models.ForeignKey(
        "scheduling.ClassSession",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="access_logs",
    )
    timestamp = models.DateTimeField()
    guest_name = models.CharField(max_length=150, blank=True)
    granted = models.BooleanField(default=True)
    denial_reason = models.CharField(max_length=255, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-timestamp"]
        verbose_name = "Access Log"
        verbose_name_plural = "Access Logs"

    def __str__(self):
        person = self.member or self.visitor or "Unknown"
        return f"{person} ({self.direction}) @ {self.timestamp}"


class AccessCard(models.Model):
    class CardType(models.TextChoices):
        RFID = "rfid", "RFID"
        QR = "qr", "QR"
        BARCODE = "barcode", "Barcode"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    member = models.ForeignKey(
        "members.MemberProfile",
        on_delete=models.CASCADE,
        related_name="access_cards",
    )
    card_number = models.CharField(max_length=255, unique=True)
    card_type = models.CharField(
        max_length=10,
        choices=CardType.choices,
    )
    is_active = models.BooleanField(default=True)
    issued_at = models.DateTimeField()
    deactivated_at = models.DateTimeField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-issued_at"]
        verbose_name = "Access Card"
        verbose_name_plural = "Access Cards"

    def __str__(self):
        return f"{self.card_number} ({self.get_card_type_display()}) - {self.member}"


class Visitor(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=150)
    phone = models.CharField(max_length=20)
    visitor_code = models.CharField(max_length=6, unique=True, editable=False)
    registered_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name="registered_visitors",
    )
    location = models.ForeignKey(
        "core.Location",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="visitors",
    )
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-created_at"]
        verbose_name = "Visitor"
        verbose_name_plural = "Visitors"

    def save(self, *args, **kwargs):
        if not self.visitor_code:
            self.visitor_code = self._generate_unique_code()
        super().save(*args, **kwargs)

    def _generate_unique_code(self):
        chars = string.ascii_uppercase + string.digits
        for _ in range(100):
            code = get_random_string(6, chars)
            if not Visitor.objects.filter(visitor_code=code).exists():
                return code
        raise ValueError("Could not generate unique visitor code")

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


class GymDoorQR(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    location = models.ForeignKey(
        "core.Location",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="door_qr_codes",
    )
    qr_payload = models.CharField(max_length=64, unique=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-created_at"]
        verbose_name = "Gym Door QR"
        verbose_name_plural = "Gym Door QRs"

    def save(self, *args, **kwargs):
        if not self.qr_payload:
            self.qr_payload = uuid.uuid4().hex[:16]
        super().save(*args, **kwargs)

    def __str__(self):
        return f"Door QR - {self.location} ({self.qr_payload})"


class TimeBasedAccessRule(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=150)
    access_point = models.ForeignKey(
        AccessPoint,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="access_rules",
    )
    location = models.ForeignKey(
        "core.Location",
        on_delete=models.CASCADE,
        related_name="access_rules",
    )
    day_of_week = models.IntegerField(
        help_text="Day of week: 0=Monday, 6=Sunday",
    )
    start_time = models.TimeField()
    end_time = models.TimeField()
    membership_plans = models.ManyToManyField(
        "members.MembershipPlan",
        blank=True,
        related_name="access_rules",
    )
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["day_of_week", "start_time"]
        verbose_name = "Time-Based Access Rule"
        verbose_name_plural = "Time-Based Access Rules"

    def __str__(self):
        days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
        day_name = days[self.day_of_week] if 0 <= self.day_of_week <= 6 else "?"
        return f"{self.name} - {day_name} {self.start_time:%H:%M}-{self.end_time:%H:%M}"


# ---------------------------------------------------------------------------
# Inventory / Equipment
# ---------------------------------------------------------------------------


class EquipmentCategory(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='gym_equipment_categories',
    )
    name = models.CharField(max_length=150)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["name"]
        verbose_name = "Equipment Category"
        verbose_name_plural = "Equipment Categories"

    def __str__(self):
        return self.name


class Equipment(models.Model):
    class Status(models.TextChoices):
        OPERATIONAL = "operational", "Operational"
        MAINTENANCE = "maintenance", "Under Maintenance"
        OUT_OF_ORDER = "out_of_order", "Out of Order"
        RETIRED = "retired", "Retired"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='gym_equipment',
    )
    name = models.CharField(max_length=255)
    category = models.ForeignKey(
        EquipmentCategory,
        on_delete=models.CASCADE,
        related_name="equipment",
    )
    location = models.ForeignKey(
        "core.Location",
        on_delete=models.CASCADE,
        related_name="equipment",
    )
    serial_number = models.CharField(max_length=255, blank=True)
    purchase_date = models.DateField()
    purchase_price = models.DecimalField(max_digits=10, decimal_places=2)
    warranty_expiry = models.DateField(blank=True, null=True)
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.OPERATIONAL,
    )
    last_maintenance = models.DateField(blank=True, null=True)
    next_maintenance = models.DateField(blank=True, null=True)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["name"]
        verbose_name = "Equipment"
        verbose_name_plural = "Equipment"

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


class MaintenanceLog(models.Model):
    class MaintenanceType(models.TextChoices):
        PREVENTIVE = "preventive", "Preventive"
        CORRECTIVE = "corrective", "Corrective"
        INSPECTION = "inspection", "Inspection"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    equipment = models.ForeignKey(
        Equipment,
        on_delete=models.CASCADE,
        related_name="maintenance_logs",
    )
    performed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name="maintenance_logs",
    )
    maintenance_type = models.CharField(
        max_length=20,
        choices=MaintenanceType.choices,
    )
    description = models.TextField()
    cost = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    date = models.DateField()
    next_scheduled = models.DateField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-date"]
        verbose_name = "Maintenance Log"
        verbose_name_plural = "Maintenance Logs"

    def __str__(self):
        return f"{self.equipment} - {self.get_maintenance_type_display()} ({self.date})"


# ---------------------------------------------------------------------------
# POS / Products
# ---------------------------------------------------------------------------


class Product(models.Model):
    class Category(models.TextChoices):
        SUPPLEMENT = "supplement", "Supplement"
        MERCHANDISE = "merchandise", "Merchandise"
        BEVERAGE = "beverage", "Beverage"
        SNACK = "snack", "Snack"
        EQUIPMENT = "equipment", "Equipment"
        OTHER = "other", "Other"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='gym_products',
    )
    name = models.CharField(max_length=255)
    sku = models.CharField(max_length=100, unique=True)
    description = models.TextField(blank=True)
    category = models.CharField(
        max_length=20,
        choices=Category.choices,
    )
    price = models.DecimalField(max_digits=10, decimal_places=2)
    cost_price = models.DecimalField(max_digits=10, decimal_places=2)
    stock_quantity = models.PositiveIntegerField(default=0)
    reorder_level = models.PositiveIntegerField(default=0)
    image = models.ImageField(upload_to="operations/products/", blank=True, null=True)
    is_active = models.BooleanField(default=True)
    location = models.ForeignKey(
        "core.Location",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="products",
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["name"]
        verbose_name = "Product"
        verbose_name_plural = "Products"

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


class Supplier(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    gym = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='gym_suppliers',
    )
    name = models.CharField(max_length=255)
    contact_person = models.CharField(max_length=150, blank=True)
    email = models.EmailField(blank=True)
    phone = models.CharField(max_length=20, blank=True)
    address = models.TextField(blank=True)
    notes = models.TextField(blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["name"]
        verbose_name = "Supplier"
        verbose_name_plural = "Suppliers"

    def __str__(self):
        return self.name


class PurchaseOrder(models.Model):
    class Status(models.TextChoices):
        DRAFT = "draft", "Draft"
        ORDERED = "ordered", "Ordered"
        RECEIVED = "received", "Received"
        CANCELLED = "cancelled", "Cancelled"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    supplier = models.ForeignKey(
        Supplier,
        on_delete=models.CASCADE,
        related_name="purchase_orders",
    )
    order_number = models.CharField(max_length=100, unique=True)
    order_date = models.DateField()
    expected_delivery = models.DateField(blank=True, null=True)
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.DRAFT,
    )
    total_amount = models.DecimalField(max_digits=12, decimal_places=2)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-order_date"]
        verbose_name = "Purchase Order"
        verbose_name_plural = "Purchase Orders"

    def __str__(self):
        return f"{self.order_number} - {self.supplier}"


class PurchaseOrderItem(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    order = models.ForeignKey(
        PurchaseOrder,
        on_delete=models.CASCADE,
        related_name="items",
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.CASCADE,
        related_name="purchase_order_items",
    )
    quantity = models.PositiveIntegerField()
    unit_price = models.DecimalField(max_digits=10, decimal_places=2)
    received_quantity = models.PositiveIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["order"]
        verbose_name = "Purchase Order Item"
        verbose_name_plural = "Purchase Order Items"

    def __str__(self):
        return f"{self.product} x{self.quantity} (Order: {self.order.order_number})"


class POSTransaction(models.Model):
    class PaymentMethod(models.TextChoices):
        CASH = "cash", "Cash"
        CARD = "card", "Card"
        MPESA = "mpesa", "M-Pesa"
        MIXED = "mixed", "Mixed"

    class Status(models.TextChoices):
        COMPLETED = "completed", "Completed"
        REFUNDED = "refunded", "Refunded"
        VOIDED = "voided", "Voided"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    member = models.ForeignKey(
        "members.MemberProfile",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="pos_transactions",
    )
    cashier = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="pos_transactions",
    )
    location = models.ForeignKey(
        "core.Location",
        on_delete=models.CASCADE,
        related_name="pos_transactions",
    )
    subtotal = models.DecimalField(max_digits=10, decimal_places=2)
    tax = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    discount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    total = models.DecimalField(max_digits=10, decimal_places=2)
    payment_method = models.CharField(
        max_length=10,
        choices=PaymentMethod.choices,
    )
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.COMPLETED,
    )
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-created_at"]
        verbose_name = "POS Transaction"
        verbose_name_plural = "POS Transactions"

    def __str__(self):
        return f"TX-{self.id} ({self.total} - {self.get_payment_method_display()})"


class POSTransactionItem(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    transaction = models.ForeignKey(
        POSTransaction,
        on_delete=models.CASCADE,
        related_name="items",
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="pos_transaction_items",
    )
    description = models.CharField(max_length=255)
    quantity = models.PositiveIntegerField(default=1)
    unit_price = models.DecimalField(max_digits=10, decimal_places=2)
    total_price = models.DecimalField(max_digits=10, decimal_places=2)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["transaction"]
        verbose_name = "POS Transaction Item"
        verbose_name_plural = "POS Transaction Items"

    def __str__(self):
        return f"{self.description} x{self.quantity}"


class GiftCard(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    code = models.CharField(max_length=100, unique=True)
    balance = models.DecimalField(max_digits=10, decimal_places=2)
    initial_balance = models.DecimalField(max_digits=10, decimal_places=2)
    purchased_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="purchased_gift_cards",
    )
    assigned_to = models.ForeignKey(
        "members.MemberProfile",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="gift_cards",
    )
    is_active = models.BooleanField(default=True)
    expires_at = models.DateTimeField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-created_at"]
        verbose_name = "Gift Card"
        verbose_name_plural = "Gift Cards"

    def __str__(self):
        return f"{self.code} (Balance: {self.balance})"


# ---------------------------------------------------------------------------
# Bookings / Reservations
# ---------------------------------------------------------------------------


class FacilityBooking(models.Model):
    class FacilityType(models.TextChoices):
        COURT = "court", "Court"
        STUDIO = "studio", "Studio"
        POOL_LANE = "pool_lane", "Pool Lane"
        SAUNA = "sauna", "Sauna"
        LOCKER = "locker", "Locker"
        CHILDCARE = "childcare", "Childcare"
        PARKING = "parking", "Parking"

    class Status(models.TextChoices):
        CONFIRMED = "confirmed", "Confirmed"
        CANCELLED = "cancelled", "Cancelled"
        COMPLETED = "completed", "Completed"
        NO_SHOW = "no_show", "No Show"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    facility_type = models.CharField(
        max_length=20,
        choices=FacilityType.choices,
    )
    facility_name = models.CharField(max_length=255)
    member = models.ForeignKey(
        "members.MemberProfile",
        on_delete=models.CASCADE,
        related_name="facility_bookings",
    )
    location = models.ForeignKey(
        "core.Location",
        on_delete=models.CASCADE,
        related_name="facility_bookings",
    )
    start_time = models.DateTimeField()
    end_time = models.DateTimeField()
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.CONFIRMED,
    )
    price = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-start_time"]
        verbose_name = "Facility Booking"
        verbose_name_plural = "Facility Bookings"

    def __str__(self):
        return f"{self.facility_name} - {self.member} ({self.start_time:%Y-%m-%d %H:%M})"
