# payments/models.py
# from django.db import models
# from django.contrib.auth.models import User
# from django.conf import settings

# class PaymentTransaction(models.Model):
#     SERVICE_CHOICES = [
#         ('prepaid', 'Prepaid'),
#         ('recharge', 'Recharge'),
#         ('postpaid', 'Postpaid'),
#         ('electricity', 'Electricity'),
#         ('landline', 'Landline'),
#         ('broadband', 'Broadband'),
#         ('water', 'Water'),
#         ('lpg', 'LPG'),
#         ('gas', 'GAS'),
#         ('dth', 'DTH'),
#         ('loan', 'Loan'),
#         ('insurance', 'Insurance'),
#         ('fastag', 'FasTag'),
#         ('bbps', 'BBPS'),
#         ('voucher', 'Voucher'),
#         ('subscription', 'Subscription'),
#         ('other', 'Other'),
#     ]

#     STATUS_CHOICES = [
#         ('notrequired', 'NotRequired'),
#         ('initiating', 'Initiating'),
#         ('initiated', 'Initiated'),
#         ('verified', 'Verified'),
#         ('success', 'Success'),
#         ('failed', 'Failed'),
#         ('error', 'Error'),
#         ('cancelled', 'Cancelled'),
#     ]

#     user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#     service = models.CharField(max_length=30, choices=SERVICE_CHOICES, default='other')
#     amount = models.DecimalField(max_digits=10, decimal_places=2)
#     wallet_amount = models.DecimalField(max_digits=10, decimal_places=2)
#     gateway_amount = models.DecimalField(max_digits=10, decimal_places=2)
#     order_id = models.CharField(max_length=100, unique=True)
#     razorpay_order_id = models.CharField(max_length=100, null=True, blank=True, unique=True)
#     razorpay_payment_id = models.CharField(max_length=100, blank=True, null=True)
#     razorpay_signature = models.TextField(blank=True, null=True)
#     status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="initiated")
#     wallet_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="notrequired")
#     created_at = models.DateTimeField(auto_now_add=True)
#     updated_at = models.DateTimeField(auto_now=True)
#     metadata = models.JSONField(blank=True, null=True)  # 🆕 Optional extra info per use case

#     def __str__(self):
#         return f"{self.user} | INR{self.amount} | {self.status} | {self.service}"



# payments/models.py
from django.db import models
from django.conf import settings

class PaymentTransaction(models.Model):
    SERVICE_CHOICES = [
        ('prepaid', 'Prepaid'),
        ('recharge', 'Recharge'),
        ('postpaid', 'Postpaid'),
        ('electricity', 'Electricity'),
        ('landline', 'Landline'),
        ('broadband', 'Broadband'),
        ('water', 'Water'),
        ('lpg', 'LPG'),
        ('gas', 'GAS'),
        ('dth', 'DTH'),
        ('loan', 'Loan'),
        ('insurance', 'Insurance'),
        ('fastag', 'FasTag'),
        ('bbps', 'BBPS'),
        ('voucher', 'Voucher'),
        ('subscription', 'Subscription'),
        ('other', 'Other'),
    ]

    STATUS_CHOICES = [
        ('notrequired', 'NotRequired'),
        ('initiating', 'Initiating'),
        ('initiated', 'Initiated'),
        ('verified', 'Verified'),
        ('success', 'Success'),
        ('failed', 'Failed'),
        ('error', 'Error'),
        ('cancelled', 'Cancelled'),
        ('processing', 'Processing'),
        ('processed', 'Processed'),
    ]

    # NEW: specific gateway method types (keep extensible)
    GATEWAY_METHOD_CHOICES = [
        ('upi', 'UPI'),
        ('card', 'Card'),
        ('netbanking', 'NetBanking'),
        ('wallet', 'Wallet'),
        ('emi', 'EMI'),
        ('paylater', 'PayLater'),
        ('other', 'Other'),
    ]

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    service = models.CharField(max_length=30, choices=SERVICE_CHOICES, default='other')

    amount = models.DecimalField(max_digits=10, decimal_places=2)
    wallet_amount = models.DecimalField(max_digits=10, decimal_places=2)
    gateway_amount = models.DecimalField(max_digits=10, decimal_places=2)

    order_id = models.CharField(max_length=100, unique=True)

    # Razorpay fields
    razorpay_order_id = models.CharField(max_length=100, null=True, blank=True, unique=True)
    razorpay_payment_id = models.CharField(max_length=100, blank=True, null=True)
    razorpay_signature = models.TextField(blank=True, null=True)

    # Existing statuses (keep as-is; 'status' currently used for gateway in your flows)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="initiated")
    wallet_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="notrequired")

    # NEW: dedicated gateway status (don’t disturb 'status' now; migrate later)
    gateway_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="initiated")

    # NEW: refund booleans
    wallet_is_refunded = models.BooleanField(default=False)
    gateway_is_refunded = models.BooleanField(default=False)
    refund_reason = models.CharField(max_length=255, blank=True, null=True)
    wallet_refunded_on = models.DateTimeField(blank=True, null=True)
    gateway_refunded_on = models.DateTimeField(blank=True, null=True)
    gateway_refunded_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="initiated")

    # NEW: gateway payment method details
    gateway_method = models.CharField(max_length=20, choices=GATEWAY_METHOD_CHOICES, null=True, blank=True)
    gateway_upi_address = models.CharField(max_length=255, null=True, blank=True)  # e.g. name@bank

    # Timestamps & misc
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    metadata = models.JSONField(blank=True, null=True)

    class Meta:
        indexes = [
            models.Index(fields=['user', 'created_at']),
            models.Index(fields=['order_id']),
            models.Index(fields=['status', 'created_at']),
            models.Index(fields=['wallet_status', 'created_at']),
            models.Index(fields=['gateway_status', 'created_at']),  # NEW
        ]

    def __str__(self):
        return f"{self.user} | INR{self.amount} | {self.status} | {self.service}"

    def save(self, *args, **kwargs):
        # Until legacy `status` is removed, mirror it into `gateway_status`
        if self.status and self.gateway_status != self.status:
            self.gateway_status = self.status
        super().save(*args, **kwargs)


class RechargePaymentSummary(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    # Money as Decimal (lossless)
    recharge_amount       = models.DecimalField(max_digits=12, decimal_places=2)
    used_external_wallet  = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    used_internal_wallet  = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    paid_via_gateway      = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    amount_paise          = models.PositiveIntegerField(default=0)

    recharge_status       = models.CharField(max_length=20, default="initiated")
    gateway_reference     = models.CharField(max_length=150, blank=True, null=True)
    status_message        = models.CharField(max_length=150, blank=True, null=True)
    full_response         = models.JSONField(null=True, blank=True)
    
    order_id              = models.CharField(max_length=100, unique=True)
    is_refunded           = models.BooleanField(default=False)
    updated_at            = models.DateTimeField(auto_now=True)
    refund_reason         = models.CharField(max_length=255, blank=True, null=True)
    refunded_on           = models.DateTimeField(blank=True, null=True)

    # Provider extras
    provider_order_id     = models.CharField(max_length=100, blank=True, null=True, db_index=True)  # orderId
    operator_order_id     = models.CharField(max_length=100, blank=True, null=True, db_index=True)  # RefId
    provider_service      = models.CharField(max_length=50, blank=True, null=True)                  # Service
    provider_commission   = models.DecimalField(max_digits=12, decimal_places=3, blank=True, null=True)  # Comi (e.g., 2.990)
    provider_request_time = models.DateTimeField(blank=True, null=True)                            # reqTime
    provider_voucher_code = models.CharField(max_length=100, blank=True, null=True)                # VoucherCode

    
class ExternalWallet(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    balance = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    last_updated = models.DateTimeField(auto_now=True)

    def increase(self, amount):
        self.balance += Decimal(amount)
        self.save()

    def decrease(self, amount):
        if self.balance >= Decimal(amount):
            self.balance -= Decimal(amount)
            self.save()
        else:
            raise ValueError("Insufficient external wallet balance.")

    def __str__(self):
        return f"{self.user.mobile_number} | INR{self.balance}"

class ExternalWalletTransaction(models.Model):
    TRANSACTION_TYPE_CHOICES = [
        ("credit", "Credit"),
        ("debit", "Debit"),
    ]

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='external_wallet_transactions')
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    transaction_type = models.CharField(max_length=10, choices=TRANSACTION_TYPE_CHOICES)
    purpose = models.CharField(max_length=100)
    order_id = models.CharField(max_length=100, blank=True, null=True)

    # Admin who added the credit (for top-ups)
    reference_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        related_name='external_wallet_admin',
        on_delete=models.SET_NULL
    )

    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        sign = '+' if self.transaction_type == 'credit' else '-'
        return f"{self.user.mobile_number} | {sign}INR{self.amount} | {self.purpose}"


class ExternalWalletTopUp(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    added_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name='external_topup_admin')
    added_on = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        user_name = str(self.user)
        added_by_name = str(self.added_by) if self.added_by else "Unknown"
        return f"{user_name} | +INR{self.amount} | by {added_by_name}"

class ExternalWalletUsage(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    amount_used = models.DecimalField(max_digits=10, decimal_places=2)
    used_for = models.CharField(max_length=100)  # e.g. "Recharge", "Utility Payment"
    used_on = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.user.mobile_number} | -INR{self.amount_used}"

# payments/models.py
from decimal import Decimal, ROUND_HALF_EVEN

class ViralPeWallet(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    balance = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    last_updated = models.DateTimeField(auto_now=True)

    # def increase(self, amount):
    #     self.balance += Decimal(amount)
    #     self.save()
    def __str__(self):
        return f"{self.user.mobile_number} | INR{self.balance}"

    def _quant2(self, x):
        # Always quantize to 2dp using banker’s rounding
        return Decimal(str(x)).quantize(Decimal('0.00'), rounding=ROUND_HALF_EVEN)

    def increase(self, amount):
        """Non-concurrent-safe helper (use service below for real updates)."""
        amt = self._quant2(amount)
        self.balance = self._quant2(self.balance + amt)
        self.save(update_fields=["balance", "last_updated"])

class ViralPeWalletUsage(models.Model):
    TRANSACTION_TYPE_CHOICES = [
        ("credit", "Credit"),
        ("debit", "Debit"),
    ]
    PURPOSE_CHOICES = [
        ("manual_test", "Manual Top-Up (Testing)"),
        ("manual_admin", "Manual Top-Up (Admin)"),
        ("oneapp_withdrawal", "Credit from 1App Withdrawal"),
        ("payment_auto_refund", "Payment Auto Refund"),
        ("payment_manual_refund", "Payment Manual Refund"),
        ("other", "Other"),
    ]

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='wallet_usages')
    amount_used = models.DecimalField(max_digits=10, decimal_places=2)
    # transaction_type = models.CharField(max_length=10, choices=TRANSACTION_TYPE_CHOICES)
    transaction_type = models.CharField(max_length=10, choices=TRANSACTION_TYPE_CHOICES, default="credit")
    purpose = models.CharField(max_length=100)
    purpose_code = models.CharField(max_length=32, choices=PURPOSE_CHOICES, default="manual_admin")
    purpose_note = models.CharField(max_length=200, blank=True, default="")
    order_id = models.CharField(max_length=100, blank=True, null=True, db_index=True)
    # order_id = models.CharField(max_length=100, blank=True, null=True)
    reference_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        related_name='credited_by',
        on_delete=models.SET_NULL
    )
    used_on = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        sign = '+' if self.transaction_type == 'credit' else '-'
        return f"{self.user.mobile_number} | {sign}INR{self.amount_used} | {self.purpose}"


# 💼 Commission Models, Views, URLs, and Pages for ViralPe

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone

# ----------------------------
# 1. Commission Config
# ----------------------------
# In payments/models.py

# from django.core.exceptions import ValidationError
# from django.conf import settings
# from django.db import models
# from recharge.models import Operator

# class CommissionConfig(models.Model):
#     operator = models.ForeignKey(Operator, on_delete=models.CASCADE)
#     provider_name = models.CharField(max_length=100)
#     provider_percentage = models.FloatField(default=0.0)
#     fixed_percentage = models.FloatField(default=0.0)
#     created_at = models.DateTimeField(auto_now_add=True)
#     updated_at = models.DateTimeField(auto_now=True)

#     class Meta:
#         unique_together = ('operator', 'provider_name')

#     def clean(self):
#         # if operator not chosen yet, skip this cross-field validation
#         if not getattr(self, "operator_id", None):
#             return
#         src = settings.PROVIDER_SOURCE_MAP.get(self.provider_name, self.provider_name)
#         # Use .operator_id guard above; now it’s safe to access .operator
#         if self.operator and (self.operator.source or "").lower() != str(src).lower():
#             raise ValidationError({
#                 'operator': (
#                     f"Selected operator source '{self.operator.source}' "
#                     f"does not match provider '{src}'."
#                 )
#             })

#     def __str__(self):
#         return f"{getattr(self, 'operator', None) or '—'} - {self.fixed_percentage}%"
from decimal import Decimal, ROUND_HALF_UP
from django.core.exceptions import ValidationError
from django.conf import settings
from django.db import models
from recharge.models import Operator

class CommissionConfig(models.Model):
    TYPE_PERCENT = "PERCENT"
    TYPE_FLAT = "FLAT"
    COMMISSION_TYPE_CHOICES = [
        (TYPE_PERCENT, "Percentage"),   # value = % of amount (0–100)
        (TYPE_FLAT, "Flat"),            # value = absolute currency (₹)
    ]

    operator = models.ForeignKey(Operator, on_delete=models.CASCADE)
    provider_name = models.CharField(max_length=100)

    # New normalized fields
    commission_type = models.CharField(
        max_length=8,
        choices=COMMISSION_TYPE_CHOICES,
        default=TYPE_PERCENT,
    )
    commission_value = models.DecimalField(
        max_digits=9,
        decimal_places=4,
        default=Decimal("0.00"),
        help_text="If type=Percentage → store percent (e.g., 2.5). If type=Flat → store ₹ amount (e.g., 3.00).",
    )

    # (Optional legacy fields kept for now; plan to remove later)
    provider_percentage = models.FloatField(default=0.0, editable=False)
    fixed_percentage = models.FloatField(default=0.0, editable=False)

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

    class Meta:
        unique_together = ('operator', 'provider_name', 'commission_type')
        indexes = [
            models.Index(fields=['operator', 'provider_name']),
        ]

    def clean(self):
        # 1) Existing cross-field validation for provider/operator source
        if not getattr(self, "operator_id", None):
            return
        src = settings.PROVIDER_SOURCE_MAP.get(self.provider_name, self.provider_name)
        if self.operator and (self.operator.source or "").lower() != str(src).lower():
            raise ValidationError({
                'operator': (
                    f"Selected operator source '{self.operator.source}' "
                    f"does not match provider '{src}'."
                )
            })

        # 2) Commission value semantics
        if self.commission_type == self.TYPE_PERCENT:
            if self.commission_value < 0 or self.commission_value > 100:
                raise ValidationError({'commission_value': "Percentage must be between 0 and 100."})
        elif self.commission_type == self.TYPE_FLAT:
            if self.commission_value < 0:
                raise ValidationError({'commission_value': "Flat commission cannot be negative."})

    def calc_commission(self, base_amount: Decimal) -> Decimal:
        """
        Returns the commission amount in ₹ for a given base_amount in ₹.
        Rounds to 2 decimals (banker-friendly).
        """
        base_amount = Decimal(base_amount)
        if self.commission_type == self.TYPE_PERCENT:
            amt = (base_amount * (self.commission_value / Decimal("100"))).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
        else:  # FLAT
            amt = Decimal(self.commission_value).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
        return amt

    @property
    def is_percentage(self) -> bool:
        return self.commission_type == self.TYPE_PERCENT

    @property
    def is_flat(self) -> bool:
        return self.commission_type == self.TYPE_FLAT

    def __str__(self):
        unit = "%" if self.is_percentage else "₹"
        return f"{getattr(self, 'operator', None) or '—'} / {self.provider_name} → {self.commission_value}{unit}"


# ----------------------------
# 2. Commission Split (Role-wise Split)
# ----------------------------
from django.core.exceptions import ValidationError
from decimal import Decimal, ROUND_HALF_UP

class CommissionSplitConfig(models.Model):
    WALLET_CHOICES = [
        ('gst', 'GST'),
        ("user", "User"),
        ("referral", "Referral"),
        ("vendor_ref", "Vendor Referral"),
        ("pincode", "Pincode Head"),
        ("district", "District Head"),
        ("state", "State Head"),
        ("vertical", "Vertical Head"),
        ("TnM", "T&M Account"),
        ("company", "Company Account")
    ]
    # commission_config = models.ForeignKey(CommissionConfig, on_delete=models.CASCADE)
    role = models.CharField(max_length=20, choices=WALLET_CHOICES)
    percentage = models.FloatField()  # part of the total commission
    created_at = models.DateTimeField(auto_now_add=True)
    
    # def clean(self):
    #     total = CommissionSplitConfig.objects.exclude(pk=self.pk).aggregate(
    #         models.Sum('percentage')
    #     )['percentage__sum'] or 0

    #     if total + self.percentage > 100:
    #         raise ValidationError("Total split across all roles cannot exceed 100%.")
    def clean(self):
        total = (CommissionSplitConfig.objects
                 .exclude(pk=self.pk)
                 .aggregate(s=models.Sum('percentage'))['s'] or 0.0)

        # use Decimal for the comparison with a tiny tolerance
        total_dec = (Decimal(str(total)) + Decimal(str(self.percentage))).quantize(Decimal('0.0001'), rounding=ROUND_HALF_UP)
        if total_dec > Decimal('100.0001'):  # small epsilon
            raise ValidationError("Total split across all roles cannot exceed 100%.")







# ----------------------------
# 3. Commission Log
# ----------------------------
class CommissionLog(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='commission_logs')
    transaction_id = models.CharField(max_length=100)
    service_type = models.CharField(max_length=50)
    amount = models.FloatField()  # Commission amount
    role = models.CharField(max_length=20)  # user, referral, vendor_ref, pincode, etc.
    region_type = models.CharField(max_length=20, blank=True)  # pincode, district, state, vertical
    region_value = models.CharField(max_length=50, blank=True)
    timestamp = models.DateTimeField(default=timezone.now)
    is_credited = models.BooleanField(default=False)
    approved_at = models.DateTimeField(null=True, blank=True)


# ----------------------------
# 4. Geo Role Assignment (Historical Tracking)
# ----------------------------
from django.core.exceptions import ValidationError
from django.utils import timezone
class GeoRoleAssignment(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    role = models.CharField(max_length=20, choices=[
        ("pincode", "Pincode Head"),
        ("district", "District Head"),
        ("state", "State Head"),
        ("vertical", "Vertical Head")
    ])
    pincode = models.CharField(max_length=10, blank=True)
    district = models.CharField(max_length=100, blank=True)
    state = models.CharField(max_length=100, blank=True)
    vertical = models.CharField(max_length=100, blank=True)
    valid_from = models.DateField()
    valid_to = models.DateField(null=True, blank=True)
    assigned_at = models.DateTimeField(auto_now_add=True)

    def clean(self):
        # Ensure only relevant location field is filled based on role
        if self.role == 'pincode':
            if not self.pincode:
                raise ValidationError("Pincode must be set for Pincode Head.")
            if self.district or self.state or self.vertical:
                raise ValidationError("Only Pincode should be set for Pincode Head.")
        elif self.role == 'district':
            if not self.district:
                raise ValidationError("District must be set for District Head.")
            if self.pincode or self.state or self.vertical:
                raise ValidationError("Only District should be set for District Head.")
        elif self.role == 'state':
            if not self.state:
                raise ValidationError("State must be set for State Head.")
            if self.pincode or self.district or self.vertical:
                raise ValidationError("Only State should be set for State Head.")
        elif self.role == 'vertical':
            if not self.vertical:
                raise ValidationError("Vertical must be set for Vertical Head.")
            if self.pincode or self.district or self.state:
                raise ValidationError("Only Vertical should be set for Vertical Head.")

        # Overlapping check
        qs = GeoRoleAssignment.objects.filter(
            role=self.role,
            pincode=self.pincode,
            district=self.district,
            state=self.state,
            vertical=self.vertical,
        ).exclude(pk=self.pk)

        for existing in qs:
            if (self.valid_to or timezone.now().date()) >= existing.valid_from and \
            (existing.valid_to or timezone.now().date()) >= self.valid_from:
                raise ValidationError("Overlapping role assignment for the selected region.")



# ----------------------------
# 5. Geo Commission Wallets
# ----------------------------
class GeoCommissionWallet(models.Model):
    region_type = models.CharField(max_length=50)  # pincode, district, state, vertical
    region_value = models.CharField(max_length=100)  # actual code/ID
    balance = models.DecimalField(max_digits=12, decimal_places=2, default=0.00)
    last_updated = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("region_type", "region_value")

class CommissionPayoutHistory(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    region_type = models.CharField(max_length=50)
    region_value = models.CharField(max_length=100)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    paid_on = models.DateTimeField(auto_now_add=True)
    paid_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='commission_paid_by', on_delete=models.SET_NULL, null=True)
