"""Views for payment deposit and withdrawal operations."""

from __future__ import annotations

from decimal import Decimal

from django.db import transaction
from django.utils import timezone
from drf_spectacular.utils import OpenApiResponse, extend_schema
from rest_framework import permissions, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from apps.transactions.models import Transaction

from .models import Deposit, PaymentAddress, WithdrawalRequest
from .serializers import (
    DepositSerializer,
    PaymentAddressSerializer,
    WithdrawalRequestSerializer,
)


class PaymentAddressViewSet(viewsets.ReadOnlyModelViewSet):
    """View and request deposit addresses."""

    queryset = PaymentAddress.objects.none()
    serializer_class = PaymentAddressSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        return PaymentAddress.objects.filter(
            user=self.request.user,
            status=PaymentAddress.STATUS_ACTIVE
        ).order_by("-created_at")

    @extend_schema(
        operation_id="payment_addresses_get_or_create",
        responses={
            200: PaymentAddressSerializer,
            201: PaymentAddressSerializer,
        },
    )
    @action(detail=False, methods=["post"], url_path="get-or-create")
    def get_or_create_address(self, request):
        """
        Get or create a deposit address for the specified blockchain.
        
        For now, this creates a placeholder address. In production, this would
        call the payment gateway API to generate a real address.
        """
        blockchain = request.data.get("blockchain", "tron").lower()
        
        if blockchain not in ["tron", "bnb"]:
            return Response(
                {"error": "Only 'tron' and 'bnb' networks are supported"},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Try to find existing active address
        existing = PaymentAddress.objects.filter(
            user=request.user,
            blockchain=blockchain,
            status=PaymentAddress.STATUS_ACTIVE
        ).first()
        
        if existing:
            serializer = self.get_serializer(existing)
            return Response(serializer.data, status=status.HTTP_200_OK)
        
        # Create new address (placeholder for now)
        # In production, call payment gateway API here
        import hashlib
        import time
        
        # Generate a placeholder address based on user ID and blockchain
        placeholder = f"{request.user.id}_{blockchain}_{int(time.time())}"
        address_hash = hashlib.sha256(placeholder.encode()).hexdigest()[:42]
        
        if blockchain == "tron":
            address = f"T{address_hash}"
        else:  # bnb
            address = f"0x{address_hash}"
        
        payment_address = PaymentAddress.objects.create(
            user=request.user,
            blockchain=blockchain,
            asset="USDT",
            address=address,
            status=PaymentAddress.STATUS_ACTIVE,
            label=f"Deposit address for {request.user.first_name}",
            metadata={"generated_by": "system", "placeholder": True}
        )
        
        serializer = self.get_serializer(payment_address)
        return Response(serializer.data, status=status.HTTP_201_CREATED)


class DepositViewSet(viewsets.ReadOnlyModelViewSet):
    """View deposit history."""

    queryset = Deposit.objects.none()
    serializer_class = DepositSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        return Deposit.objects.filter(user=self.request.user).order_by("-detected_at")


class WithdrawalRequestViewSet(viewsets.ModelViewSet):
    """Create and view withdrawal requests."""

    queryset = WithdrawalRequest.objects.none()
    serializer_class = WithdrawalRequestSerializer
    permission_classes = [permissions.IsAuthenticated]
    http_method_names = ["get", "post", "delete"]  # Allow cancel via DELETE

    def get_queryset(self):
        return WithdrawalRequest.objects.filter(user=self.request.user).order_by("-requested_at")

    @extend_schema(
        operation_id="withdrawal_requests_create",
        responses={
            201: WithdrawalRequestSerializer,
            400: OpenApiResponse(
                description="Validation error (insufficient balance, invalid amount, etc.)"
            ),
        },
    )
    def create(self, request, *args, **kwargs):
        """Create a withdrawal request and deduct balance."""
        return super().create(request, *args, **kwargs)

    @extend_schema(
        operation_id="withdrawal_requests_cancel",
        responses={
            200: OpenApiResponse(description="Withdrawal cancelled successfully"),
            400: OpenApiResponse(description="Cannot cancel withdrawal in current status"),
            404: OpenApiResponse(description="Withdrawal not found"),
        },
    )
    def destroy(self, request, *args, **kwargs):
        """Cancel a pending withdrawal request and refund balance."""
        withdrawal = self.get_object()
        
        # Only allow cancellation if status is REQUESTED
        if withdrawal.status != WithdrawalRequest.STATUS_REQUESTED:
            return Response(
                {"error": f"Cannot cancel withdrawal with status '{withdrawal.status}'"},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        with transaction.atomic():
            # Lock user and withdrawal
            user_locked = withdrawal.user.__class__.objects.select_for_update().get(pk=withdrawal.user.pk)
            withdrawal_locked = WithdrawalRequest.objects.select_for_update().get(pk=withdrawal.pk)
            
            # Get the total deduction amount from metadata
            total_deduction = withdrawal_locked.metadata.get("total_deduction", "0")
            
            # Refund the amount
            balance_before = user_locked.credit_balance
            user_locked.credit_balance += Decimal(total_deduction)
            user_locked.save(update_fields=["credit_balance"])
            
            # Update the linked transaction if it exists
            if withdrawal_locked.transaction:
                withdrawal_locked.transaction.status = Transaction.STATUS_CANCELLED
                withdrawal_locked.transaction.save(update_fields=["status", "updated_at"])
            
            # Create a refund transaction record
            Transaction.objects.create(
                user=user_locked,
                transaction_type=Transaction.TYPE_WITHDRAWAL,
                amount=Decimal(total_deduction),
                status=Transaction.STATUS_CANCELLED,
                balance_before=balance_before,
                balance_after=user_locked.credit_balance,
                data={
                    "withdrawal_id": withdrawal_locked.id,
                    "reason": "User cancelled withdrawal",
                },
            )
            
            # Update withdrawal status
            withdrawal_locked.status = WithdrawalRequest.STATUS_CANCELLED
            withdrawal_locked.processed_at = timezone.now()
            withdrawal_locked.save(update_fields=["status", "processed_at"])
        
        return Response(
            {"message": "Withdrawal cancelled successfully"},
            status=status.HTTP_200_OK
        )
