"""Payment gateway contracts for deposits, withdrawals, and callbacks."""

from __future__ import annotations

from django.db import models


class PaymentAddress(models.Model):
    """Reusable deposit address allocated to a user on a specific network."""

    STATUS_ACTIVE = "active"
    STATUS_REVOKED = "revoked"
    STATUS_EXPIRED = "expired"
    STATUS_CHOICES = [
        (STATUS_ACTIVE, "Active"),
        (STATUS_REVOKED, "Revoked"),
        (STATUS_EXPIRED, "Expired"),
    ]

    user = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
        related_name="payment_addresses",
    )
    blockchain = models.CharField(max_length=50, db_index=True)
    asset = models.CharField(max_length=20, default="USDT")
    address = models.CharField(max_length=255, unique=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_ACTIVE)
    label = models.CharField(
        max_length=100,
        blank=True,
        help_text="Optional label or derivation path reference from the gateway.",
    )
    expires_at = models.DateTimeField(null=True, blank=True)
    metadata = models.JSONField(default=dict, blank=True)

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

    class Meta:
        db_table = "payment_addresses"
        indexes = [
            models.Index(fields=["user", "blockchain"]),
            models.Index(fields=["status", "expires_at"]),
        ]

    def __str__(self) -> str:
        return f"{self.blockchain}:{self.asset}:{self.address}"


class Deposit(models.Model):
    """Inbound payment detected by the gateway; links to on-chain tx hash."""

    STATUS_PENDING = "pending"
    STATUS_CONFIRMED = "confirmed"
    STATUS_FAILED = "failed"
    STATUS_DUPLICATE = "duplicate"
    STATUS_CHOICES = [
        (STATUS_PENDING, "Pending"),
        (STATUS_CONFIRMED, "Confirmed"),
        (STATUS_FAILED, "Failed"),
        (STATUS_DUPLICATE, "Duplicate"),
    ]

    user = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
        related_name="deposits",
    )
    address = models.ForeignKey(
        PaymentAddress,
        on_delete=models.PROTECT,
        related_name="deposits",
    )
    transaction = models.OneToOneField(
        "transactions.Transaction",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="deposit",
        help_text="Corresponding wallet transaction once balance is credited.",
    )

    blockchain = models.CharField(max_length=50, db_index=True)
    asset = models.CharField(max_length=20, default="USDT")
    amount = models.DecimalField(max_digits=20, decimal_places=8)
    transaction_hash = models.CharField(max_length=255, db_index=True)
    confirmations = models.PositiveIntegerField(default=0)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_PENDING)
    detected_at = models.DateTimeField(auto_now_add=True)
    confirmed_at = models.DateTimeField(null=True, blank=True)

    raw_payload = models.JSONField(default=dict, blank=True)
    metadata = models.JSONField(default=dict, blank=True)

    class Meta:
        db_table = "payment_deposits"
        indexes = [
            models.Index(fields=["user", "status"]),
            models.Index(fields=["transaction_hash", "blockchain"]),
        ]
        constraints = [
            models.UniqueConstraint(
                fields=["transaction_hash", "blockchain"],
                name="unique_payment_deposit_tx",
            )
        ]

    def __str__(self) -> str:
        return f"Deposit {self.blockchain} {self.asset} {self.amount}"


class WithdrawalRequest(models.Model):
    """Outbound payout request to the payment gateway."""

    STATUS_REQUESTED = "requested"
    STATUS_PROCESSING = "processing"
    STATUS_SENT = "sent"
    STATUS_FAILED = "failed"
    STATUS_CANCELLED = "cancelled"
    STATUS_CHOICES = [
        (STATUS_REQUESTED, "Requested"),
        (STATUS_PROCESSING, "Processing"),
        (STATUS_SENT, "Sent"),
        (STATUS_FAILED, "Failed"),
        (STATUS_CANCELLED, "Cancelled"),
    ]

    user = models.ForeignKey(
        "users.User",
        on_delete=models.CASCADE,
        related_name="withdrawal_requests",
    )
    transaction = models.OneToOneField(
        "transactions.Transaction",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="withdrawal_request",
        help_text="Wallet transaction when the withdrawal is debited.",
    )

    blockchain = models.CharField(max_length=50, db_index=True)
    asset = models.CharField(max_length=20, default="USDT")
    amount = models.DecimalField(max_digits=20, decimal_places=8)
    destination_address = models.CharField(max_length=255)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_REQUESTED)

    gateway_reference = models.CharField(
        max_length=255,
        blank=True,
        db_index=True,
        help_text="External reference or request id from the payment gateway.",
    )
    transaction_hash = models.CharField(
        max_length=255,
        blank=True,
        db_index=True,
        help_text="On-chain hash when available.",
    )
    requested_at = models.DateTimeField(auto_now_add=True)
    processed_at = models.DateTimeField(null=True, blank=True)

    metadata = models.JSONField(default=dict, blank=True)

    class Meta:
        db_table = "payment_withdrawals"
        indexes = [
            models.Index(fields=["user", "status"]),
            models.Index(fields=["gateway_reference"]),
            models.Index(fields=["transaction_hash", "blockchain"]),
        ]

    def __str__(self) -> str:
        return f"Withdrawal {self.blockchain} {self.asset} {self.amount}"


class GatewayCallbackLog(models.Model):
    """Audit log of inbound webhook callbacks to aid troubleshooting."""

    STATUS_PENDING = "pending"
    STATUS_PROCESSED = "processed"
    STATUS_ERROR = "error"
    STATUS_CHOICES = [
        (STATUS_PENDING, "Pending"),
        (STATUS_PROCESSED, "Processed"),
        (STATUS_ERROR, "Error"),
    ]

    event_type = models.CharField(max_length=100, db_index=True)
    related_deposit = models.ForeignKey(
        Deposit,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="callback_logs",
    )
    related_withdrawal = models.ForeignKey(
        WithdrawalRequest,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="callback_logs",
    )
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_PENDING)
    error_message = models.TextField(blank=True)

    payload = models.JSONField(default=dict, blank=True)
    headers = models.JSONField(default=dict, blank=True)
    received_at = models.DateTimeField(auto_now_add=True)
    processed_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        db_table = "payment_callback_logs"
        indexes = [
            models.Index(fields=["event_type", "status"]),
            models.Index(fields=["received_at"]),
        ]

    def __str__(self) -> str:
        return f"Callback {self.event_type} [{self.status}]"
