from django.db import models
from django.conf import settings
from django.utils import timezone
from cryptography.fernet import Fernet
import base64


def get_fernet():
    """Get Fernet instance for encryption/decryption"""
    key = getattr(settings, 'MPESA_ENCRYPTION_KEY', settings.SECRET_KEY[:32])
    # Ensure key is 32 bytes and base64 encoded
    key_bytes = key.encode()[:32].ljust(32, b'0')
    key_b64 = base64.urlsafe_b64encode(key_bytes)
    return Fernet(key_b64)


class SystemSettings(models.Model):
    """System-wide settings managed by admin"""
    key = models.CharField(max_length=100, unique=True)
    value = models.TextField()
    description = models.TextField(blank=True)
    updated_at = models.DateTimeField(auto_now=True)
    updated_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )

    class Meta:
        db_table = 'system_settings'
        verbose_name_plural = 'System Settings'

    def __str__(self):
        return f"{self.key}: {self.value}"

    @classmethod
    def get_value(cls, key, default=None):
        """Get a setting value by key"""
        try:
            setting = cls.objects.get(key=key)
            return setting.value
        except cls.DoesNotExist:
            return default

    @classmethod
    def set_value(cls, key, value, description='', user=None):
        """Set a setting value"""
        setting, created = cls.objects.update_or_create(
            key=key,
            defaults={
                'value': str(value),
                'description': description,
                'updated_by': user
            }
        )
        return setting


class Payment(models.Model):
    """Model to track all payments"""
    PAYMENT_TYPE_CHOICES = (
        ('vendor_upgrade', 'Vendor Upgrade'),
        ('vendor_verification', 'Vendor Verification'),
        ('featured_product', 'Featured Product'),
        ('sponsored_ad', 'Sponsored Ad'),
        ('other', 'Other'),
    )

    STATUS_CHOICES = (
        ('pending', 'Pending'),
        ('success', 'Success'),
        ('failed', 'Failed'),
        ('abandoned', 'Abandoned'),
    )

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='payments'
    )
    payment_type = models.CharField(max_length=20, choices=PAYMENT_TYPE_CHOICES)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    currency = models.CharField(max_length=3, default='KES')
    reference = models.CharField(max_length=100, unique=True)
    paystack_reference = models.CharField(max_length=100, blank=True, null=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')

    # Paystack response data
    paystack_response = models.JSONField(blank=True, null=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    paid_at = models.DateTimeField(blank=True, null=True)

    class Meta:
        db_table = 'payments'
        ordering = ['-created_at']

    def __str__(self):
        return f"{self.user.full_name} - {self.payment_type} - {self.status}"


class VendorSubscription(models.Model):
    """Track vendor upgrade subscriptions"""
    PLAN_TYPE_CHOICES = (
        ('monthly', 'Monthly'),
        ('yearly', 'Yearly'),
        ('admin', 'Admin Granted'),
    )

    vendor = models.OneToOneField(
        'accounts.VendorProfile',
        on_delete=models.CASCADE,
        related_name='subscription'
    )
    payment = models.ForeignKey(
        Payment,
        on_delete=models.SET_NULL,
        null=True,
        related_name='subscriptions'
    )
    plan_type = models.CharField(max_length=10, choices=PLAN_TYPE_CHOICES, default='monthly')
    is_active = models.BooleanField(default=True)
    started_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField(blank=True, null=True)

    class Meta:
        db_table = 'vendor_subscriptions'

    def __str__(self):
        return f"{self.vendor.shop_name} - {'Active' if self.is_active else 'Inactive'}"

    @property
    def is_expired(self):
        return self.expires_at is not None and self.expires_at <= timezone.now()


class VerificationRequest(models.Model):
    """Track vendor verification requests"""
    STATUS_CHOICES = (
        ('pending', 'Pending'),
        ('approved', 'Approved'),
        ('rejected', 'Rejected'),
    )

    vendor = models.ForeignKey(
        'accounts.VendorProfile',
        on_delete=models.CASCADE,
        related_name='verification_requests'
    )
    payment = models.ForeignKey(
        Payment,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='verification_requests'
    )
    business_name = models.CharField(max_length=200)
    location = models.CharField(max_length=300)
    business_phone = models.CharField(max_length=20)
    social_link = models.URLField(max_length=500)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
    rejection_reason = models.TextField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'verification_requests'
        ordering = ['-created_at']

    def __str__(self):
        return f"{self.vendor.shop_name} - {self.status}"


class MpesaLinkingRequest(models.Model):
    """Store vendor M-Pesa credential requests for admin review"""
    ACCOUNT_TYPE_CHOICES = (
        ('paybill', 'Paybill'),
        ('till', 'Till Number'),
    )

    STATUS_CHOICES = (
        ('pending', 'Pending'),
        ('approved', 'Approved'),
        ('rejected', 'Rejected'),
        ('revoked', 'Revoked'),
    )

    vendor = models.ForeignKey(
        'accounts.VendorProfile',
        on_delete=models.CASCADE,
        related_name='mpesa_linking_requests'
    )
    account_type = models.CharField(max_length=10, choices=ACCOUNT_TYPE_CHOICES)
    business_short_code = models.CharField(max_length=20)

    # Encrypted credential fields
    _consumer_key_encrypted = models.BinaryField()
    _consumer_secret_encrypted = models.BinaryField()
    _passkey_encrypted = models.BinaryField()

    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
    rejection_reason = models.TextField(blank=True, null=True)

    reviewed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='reviewed_mpesa_requests'
    )
    reviewed_at = models.DateTimeField(blank=True, null=True)

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

    class Meta:
        db_table = 'mpesa_linking_requests'
        ordering = ['-created_at']

    def __str__(self):
        return f"{self.vendor.shop_name} - {self.account_type} - {self.status}"

    # Property getters/setters for encrypted fields
    @property
    def consumer_key(self):
        try:
            f = get_fernet()
            return f.decrypt(bytes(self._consumer_key_encrypted)).decode()
        except Exception:
            return None

    @consumer_key.setter
    def consumer_key(self, value):
        f = get_fernet()
        self._consumer_key_encrypted = f.encrypt(value.encode())

    @property
    def consumer_secret(self):
        try:
            f = get_fernet()
            return f.decrypt(bytes(self._consumer_secret_encrypted)).decode()
        except Exception:
            return None

    @consumer_secret.setter
    def consumer_secret(self, value):
        f = get_fernet()
        self._consumer_secret_encrypted = f.encrypt(value.encode())

    @property
    def passkey(self):
        try:
            f = get_fernet()
            return f.decrypt(bytes(self._passkey_encrypted)).decode()
        except Exception:
            return None

    @passkey.setter
    def passkey(self, value):
        f = get_fernet()
        self._passkey_encrypted = f.encrypt(value.encode())


class VendorMpesaCredentials(models.Model):
    """Active M-Pesa credentials for approved vendors"""
    ACCOUNT_TYPE_CHOICES = (
        ('paybill', 'Paybill'),
        ('till', 'Till Number'),
    )

    vendor = models.OneToOneField(
        'accounts.VendorProfile',
        on_delete=models.CASCADE,
        related_name='mpesa_credentials'
    )
    linking_request = models.OneToOneField(
        MpesaLinkingRequest,
        on_delete=models.SET_NULL,
        null=True,
        related_name='active_credentials'
    )
    account_type = models.CharField(max_length=10, choices=ACCOUNT_TYPE_CHOICES)
    business_short_code = models.CharField(max_length=20)

    # Encrypted credential fields
    _consumer_key_encrypted = models.BinaryField()
    _consumer_secret_encrypted = models.BinaryField()
    _passkey_encrypted = models.BinaryField()

    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'vendor_mpesa_credentials'

    def __str__(self):
        return f"{self.vendor.shop_name} - {self.account_type}"

    # Property getters/setters for encrypted fields
    @property
    def consumer_key(self):
        try:
            f = get_fernet()
            return f.decrypt(bytes(self._consumer_key_encrypted)).decode()
        except Exception:
            return None

    @consumer_key.setter
    def consumer_key(self, value):
        f = get_fernet()
        self._consumer_key_encrypted = f.encrypt(value.encode())

    @property
    def consumer_secret(self):
        try:
            f = get_fernet()
            return f.decrypt(bytes(self._consumer_secret_encrypted)).decode()
        except Exception:
            return None

    @consumer_secret.setter
    def consumer_secret(self, value):
        f = get_fernet()
        self._consumer_secret_encrypted = f.encrypt(value.encode())

    @property
    def passkey(self):
        try:
            f = get_fernet()
            return f.decrypt(bytes(self._passkey_encrypted)).decode()
        except Exception:
            return None

    @passkey.setter
    def passkey(self, value):
        f = get_fernet()
        self._passkey_encrypted = f.encrypt(value.encode())


class MpesaPayment(models.Model):
    """Track M-Pesa STK Push payments for products"""
    STATUS_CHOICES = (
        ('pending', 'Pending'),
        ('processing', 'Processing'),
        ('completed', 'Completed'),
        ('failed', 'Failed'),
        ('cancelled', 'Cancelled'),
        ('timeout', 'Timeout'),
    )

    # Vendor receiving payment
    vendor = models.ForeignKey(
        'accounts.VendorProfile',
        on_delete=models.CASCADE,
        related_name='mpesa_payments_received'
    )
    # Product being purchased
    product = models.ForeignKey(
        'products.Product',
        on_delete=models.SET_NULL,
        null=True,
        related_name='mpesa_payments'
    )
    # Buyer (logged in user)
    buyer = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        related_name='mpesa_payments_made'
    )

    # Payment details
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    phone_number = models.CharField(max_length=15)
    account_reference = models.CharField(max_length=50)
    transaction_desc = models.CharField(max_length=200)

    # M-Pesa tracking
    merchant_request_id = models.CharField(max_length=100, blank=True, null=True)
    checkout_request_id = models.CharField(max_length=100, blank=True, null=True, db_index=True)
    mpesa_receipt_number = models.CharField(max_length=50, blank=True, null=True, db_index=True)

    # Status tracking
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
    result_code = models.CharField(max_length=10, blank=True, null=True)
    result_desc = models.TextField(blank=True, null=True)

    # Callback data
    callback_data = models.JSONField(blank=True, null=True)

    # Timestamps
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    updated_at = models.DateTimeField(auto_now=True)
    completed_at = models.DateTimeField(blank=True, null=True)

    class Meta:
        db_table = 'mpesa_payments'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['vendor', 'status']),
            models.Index(fields=['buyer', 'status']),
            models.Index(fields=['status', 'created_at']),
        ]

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