from decimal import Decimal
from django.shortcuts import render, redirect
from django.contrib import messages
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.db import transaction
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.conf import settings
import json
import hmac
import hashlib

from rest_framework.decorators import action
from rest_framework import viewsets

from .base import DummySerializer


class PaymentViewSet(viewsets.ModelViewSet):
    """Handles Step 4: order review and payment for the Transfer Order flow."""
    serializer_class = DummySerializer

    # =========================================================================
    # Main entry point
    # =========================================================================

    @action(detail=False, methods=['get', 'post'], url_path=r'')
    def payment_view(self, request):
        """Step 4: Review delivery details, choose payment plan, pay via Paystack."""
        base_domain = request.build_absolute_uri('/')
        user = request.user

        if not user.is_authenticated:
            return redirect(f'{base_domain}login/')

        current_order_id = request.session.get('current_order_id')
        if not current_order_id:
            messages.error(request, 'Please complete storage selection first.')
            return redirect(f'{base_domain}transfer/storage/')

        try:
            from ..models import TransferOrder
            order = TransferOrder.objects.get(id=current_order_id, user=user)

            if order.status != 'storage_selected':
                messages.error(request, 'Please complete storage selection first.')
                return redirect(f'{base_domain}transfer/storage/')

            delivery_info = self._get_delivery_info(order)
            base_ctx = {
                'base_domain':          base_domain,
                'user':                 user,
                'order':                order,
                'current_step':         4,
                'selected_files_count': order.total_files,
                'total_size_gb':        float(order.total_size),
                'delivery_info':        delivery_info,
            }

            if request.method == 'POST':
                return self._handle_payment(request, order, base_ctx, base_domain)

            base_ctx.update(self._get_payment_context(order))
            return render(request, 'payment.html', base_ctx)

        except TransferOrder.DoesNotExist:
            messages.error(request, 'Order not found.')
            return redirect(f'{base_domain}transfer/new/')

    # =========================================================================
    # Delivery info extraction
    # =========================================================================

    def _get_delivery_info(self, order):
        """
        Return structured delivery details from the order.
        Prefers dedicated fields (county / town / street_address); falls back
        to parsing the legacy shipping_address string written by the storage step.
        """
        if order.county and order.town and order.street_address:
            return {
                'street_address': order.street_address,
                'district':       '',
                'town':           order.town,
                'county':         order.county,
                'additional_info': order.additional_delivery_info or '',
            }

        # Legacy format: "street, town, district, county[, Postal Code: X][, Kenya]"
        raw_lines = (order.shipping_address or '').split('\n')
        parts = [p.strip() for p in raw_lines[0].split(',')]

        street   = parts[0] if len(parts) > 0 else ''
        town     = parts[1] if len(parts) > 1 else ''
        district = parts[2] if len(parts) > 2 else ''
        county   = parts[3] if len(parts) > 3 else ''

        # Remove "Postal Code: ..." or "Kenya" if they accidentally ended up here
        for junk in ('postal', 'kenya'):
            if county.lower().startswith(junk):
                county = ''

        additional = ''
        if len(raw_lines) > 1:
            additional = '\n'.join(raw_lines[1:]).replace('Additional Instructions:', '').strip()

        return {
            'street_address': street,
            'district':       district,
            'town':           town,
            'county':         county,
            'additional_info': additional,
        }

    # =========================================================================
    # Payment context (all amounts are server-derived)
    # =========================================================================

    def _get_payment_context(self, order):
        """
        Build the template context for the payment page.
        Every KES amount is calculated server-side; none comes from the client.
        """
        base_price = order._calculate_base_price()
        zone_info  = order.get_shipping_zone_info()
        shipping   = zone_info['cost']
        tax        = order.calculate_tax(base_price + shipping)
        total      = base_price + shipping + tax
        half       = (total / Decimal('2')).quantize(Decimal('0.01'))
        balance    = (total - half).quantize(Decimal('0.01'))

        return {
            'order_summary': {
                'storage_label':  self._storage_label(order.storage_type),
                'storage_size':   order.storage_size or '—',
                'files_count':    order.total_files,
                'total_size':     f'{order.total_size:.1f} GB',
                'subtotal':       base_price,
                'shipping':       shipping,
                'tax':            tax,
                'total':          total,
                'half_amount':    half,
                'balance_due':    balance,
                'total_f':        float(total),
                'half_f':         float(half),
                'balance_f':      float(balance),
                'shipping_f':     float(shipping),
                'subtotal_f':     float(base_price),
                'tax_f':          float(tax),
            },
            'shipping_info': {
                'label':       zone_info['label'],
                'eta':         zone_info['eta'],
                'description': zone_info.get('description', ''),
                'county':      zone_info.get('county', ''),
                'is_free':     shipping == Decimal('0.00'),
            },
            'paystack_public_key': getattr(settings, 'PAYSTACK_PUBLIC_KEY', ''),
            'order_reference': f'TRF-{order.order_number}-{order.created_at.strftime("%Y%m%d%H%M%S")}',
        }

    # =========================================================================
    # POST: confirm payment after Paystack callback
    # =========================================================================

    def _handle_payment(self, request, order, ctx, base_domain):
        """
        Called after the client-side Paystack popup completes.
        The form submits the Paystack reference; we verify server-side and
        update the order. Client-submitted amounts are NEVER trusted.
        """
        reference    = request.POST.get('payment_reference', '').strip()
        payment_plan = request.POST.get('payment_plan', 'full').strip()
        email        = request.POST.get('email', '').strip()

        if payment_plan not in ('full', 'half'):
            payment_plan = 'full'

        if not reference or not email:
            messages.error(request, 'Payment could not be confirmed — please try again.')
            ctx.update(self._get_payment_context(order))
            return render(request, 'payment.html', ctx)

        # Re-derive all amounts server-side
        base_price = order._calculate_base_price()
        shipping   = order.calculate_shipping_cost()
        tax        = order.calculate_tax(base_price + shipping)
        total      = base_price + shipping + tax
        half       = (total / Decimal('2')).quantize(Decimal('0.01'))

        try:
            result = self._verify_paystack_payment(reference)

            if not result.get('status'):
                messages.error(request, f"Verification failed: {result.get('message', 'Unknown error')}")
                ctx.update(self._get_payment_context(order))
                return render(request, 'payment.html', ctx)

            with transaction.atomic():
                order.payment_method    = 'paystack'
                order.payment_reference = reference
                order.payment_plan      = payment_plan
                order.payment_amount    = total

                if payment_plan == 'half':
                    order.amount_paid = half
                    order.balance_due = total - half
                    order.status      = 'partially_paid'
                else:
                    order.amount_paid = total
                    order.balance_due = Decimal('0.00')
                    order.status      = 'payment_completed'

                order.save(update_fields=[
                    'payment_method', 'payment_reference', 'payment_plan',
                    'payment_amount', 'amount_paid', 'balance_due', 'status',
                ])

                # ========== SEND CONFIRMATION EMAIL ==========
                self._send_payment_confirmation_email(order, payment_plan, email)

            request.session.pop('current_order_id', None)

            if payment_plan == 'half':
                messages.success(
                    request,
                    f'Half payment confirmed for order {order.order_number}. '
                    f'Balance of KSh {total - half:,.0f} is due on delivery.'
                )
            else:
                messages.success(
                    request,
                    f'Payment confirmed! Order {order.order_number} is being processed.'
                )

            return redirect(f'{base_domain}transfer/orders/')

        except Exception as exc:
            messages.error(request, f'An unexpected error occurred: {exc}')
            ctx.update(self._get_payment_context(order))
            return render(request, 'payment.html', ctx)

    # =========================================================================
    # Paystack helpers (production-ready)
    # =========================================================================

    def _verify_paystack_payment(self, reference):
        """
        Verify a Paystack transaction reference via the Paystack REST API.
        Uses settings.PAYSTACK_SECRET_KEY – no mock.
        """
        secret_key = getattr(settings, 'PAYSTACK_SECRET_KEY', '')
        if not secret_key or secret_key == 'sk_live_placeholder':
            raise ValueError("PAYSTACK_SECRET_KEY is not properly configured in settings.")

        try:
            import requests as http_client
            resp = http_client.get(
                f'https://api.paystack.co/transaction/verify/{reference}',
                headers={
                    'Authorization': f'Bearer {secret_key}',
                    'Content-Type':  'application/json',
                },
                timeout=15,
            )
            data = resp.json()

            if data.get('status') and data.get('data', {}).get('status') == 'success':
                return {'status': True, 'message': 'OK', 'data': data['data']}

            return {
                'status':  False,
                'message': data.get('message', 'Verification returned non-success'),
            }

        except Exception as exc:
            return {'status': False, 'message': str(exc)}

    @csrf_exempt
    def paystack_webhook(self, request):
        """
        Handle asynchronous Paystack webhook events (e.g., charge.success).
        Signature verification is now enabled.
        """
        if request.method != 'POST':
            return JsonResponse({'status': 'invalid_method'}, status=405)

        try:
            # Signature verification – enabled for production
            sig = request.headers.get('x-paystack-signature', '')
            secret = getattr(settings, 'PAYSTACK_SECRET_KEY', '')
            if not secret or secret == 'sk_live_placeholder':
                return JsonResponse({'status': 'secret_missing'}, status=500)

            expected = hmac.new(
                secret.encode(),
                request.body,
                hashlib.sha512,
            ).hexdigest()
            if not hmac.compare_digest(sig, expected):
                return JsonResponse({'status': 'invalid_signature'}, status=401)

            payload = json.loads(request.body)
            event = payload.get('event', '')
            data = payload.get('data', {})
            reference = data.get('reference', '')

            if event == 'charge.success' and data.get('status') == 'success':
                from ..models import TransferOrder
                order = TransferOrder.objects.filter(payment_reference=reference).first()
                if order and order.status not in ('payment_completed', 'partially_paid'):
                    order.status = 'payment_completed'
                    order.save(update_fields=['status'])
                    # Optionally re-send email if status changed
                    self._send_payment_confirmation_email(order, order.payment_plan, order.user.email)
                return JsonResponse({'status': 'processed'})

            return JsonResponse({'status': 'ignored'})

        except Exception as exc:
            return JsonResponse({'status': 'error', 'message': str(exc)}, status=400)

    # =========================================================================
    # Email sending (professional HTML email)
    # =========================================================================

    def _send_payment_confirmation_email(self, order, payment_plan, recipient_email):
        """Send a detailed HTML email confirming payment and order details."""
        if not recipient_email:
            return

        # Build the same context as used in the payment page
        base_price = order._calculate_base_price()
        zone_info = order.get_shipping_zone_info()
        shipping = zone_info['cost']
        tax = order.calculate_tax(base_price + shipping)
        total = base_price + shipping + tax
        half = (total / Decimal('2')).quantize(Decimal('0.01'))
        amount_paid = half if payment_plan == 'half' else total
        balance_due = total - amount_paid

        delivery_info = self._get_delivery_info(order)

        context = {
            'customer_name': f"{order.user.first_name} {order.user.last_name}".strip() or order.user.email,
            'order_number': order.order_number,
            'payment_plan': 'Full Payment' if payment_plan == 'full' else 'Half Payment (50% now)',
            'amount_paid': f"KSh {amount_paid:,.2f}",
            'balance_due': f"KSh {balance_due:,.2f}" if balance_due > 0 else "KSh 0.00",
            'storage_type': self._storage_label(order.storage_type),
            'storage_size': order.storage_size or 'Not specified',
            'total_files': order.total_files,
            'total_size_gb': f"{order.total_size:.1f} GB",
            'subtotal': f"KSh {base_price:,.2f}",
            'shipping_cost': f"KSh {shipping:,.2f}",
            'tax': f"KSh {tax:,.2f}",
            'total_amount': f"KSh {total:,.2f}",
            'delivery_address': (
                f"{delivery_info['street_address']}<br>"
                f"{delivery_info['town']}, {delivery_info['county']}<br>"
                f"{delivery_info['additional_info']}"
            ).strip().replace('<br><br>', '<br>'),
            'shipping_method': zone_info['label'],
            'delivery_estimate': zone_info['eta'],
            'payment_reference': order.payment_reference,
            'payment_date': order.updated_at.strftime("%d %B %Y at %H:%M"),
        }

        # Render HTML email template (create 'payment_confirmation.html' in your templates)
        try:
            html_message = render_to_string('payment_confirmation.html', context)
            plain_message = f"""
                    Dear {context['customer_name']},

                    Thank you for your payment! Your order #{order.order_number} has been confirmed.

                    Payment Plan: {context['payment_plan']}
                    Amount Paid: {context['amount_paid']}
                    Remaining Balance: {context['balance_due']}

                    Order Details:
                    - Storage: {context['storage_type']} ({context['storage_size']})
                    - Files: {context['total_files']} files, {context['total_size_gb']}
                    - Subtotal: {context['subtotal']}
                    - Shipping: {context['shipping_cost']}
                    - Tax: {context['tax']}
                    - Total: {context['total_amount']}

                    Delivery Information:
                    {context['delivery_address']}
                    Shipping: {context['shipping_method']} (ETA: {context['delivery_estimate']})

                    Payment Reference: {context['payment_reference']}

                    We'll notify you when your device is ready for pickup or when shipping updates are available.

                    Thank you for choosing our service!

                    Best regards,
                    Your Transfer Team
                                """
            send_mail(
                subject=f"Payment Confirmation – Order #{order.order_number}",
                message=plain_message,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[recipient_email],
                html_message=html_message,
                fail_silently=False,
            )
        except Exception as e:
            # Log the error in production – here we simply print
            print(f"Failed to send email to {recipient_email}: {e}")

    # =========================================================================
    # Utilities
    # =========================================================================

    @staticmethod
    def _storage_label(storage_type):
        """Human-readable device name from any storage_type string."""
        return {
            'flash_drive':  'USB Flash Drive',
            'usb':          'USB Flash Drive',
            'memory_card':  'Memory Card',
            'external_hdd': 'External HDD',
            'hdd':          'Hard Disk Drive (HDD)',
            'ssd':          'Solid State Drive (SSD)',
        }.get(storage_type or '', storage_type or '—')