import csv
from io import BytesIO

from django.http import HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status, permissions, serializers
from apps.business.orders.models import Order
from apps.business.orders.serializers import OrderSerializer
from apps.business.orders.services.order_service import is_digital_product
from apps.business.wallet.models import WalletLedger, SuperCoinTransaction
from apps.business.wallet.services.wallet_service import WalletService
from apps.core.utils import error_response
from apps.business.orders.services.order_service import (
    fulfill_pending_payment_order,
    MANUAL_PAYMENT_METHODS,
)
from django.db import models
from django.utils import timezone
from decimal import Decimal


class AdminOrderSerializer(OrderSerializer):
    """Extends OrderSerializer with user info for admin views."""
    user_name = serializers.SerializerMethodField()
    user_mobile = serializers.SerializerMethodField()
    user_email = serializers.SerializerMethodField()

    def get_user_name(self, obj):
        u = getattr(obj, 'user', None)
        if not u:
            return ''
        first = (getattr(u, 'first_name', '') or '').strip()
        last = (getattr(u, 'last_name', '') or '').strip()
        return f"{first} {last}".strip() or str(u)

    def get_user_mobile(self, obj):
        return getattr(getattr(obj, 'user', None), 'mobile', '') or ''

    def get_user_email(self, obj):
        return getattr(getattr(obj, 'user', None), 'email', '') or ''

    class Meta(OrderSerializer.Meta):
        fields = OrderSerializer.Meta.fields + ['user_name', 'user_mobile', 'user_email']


def _filtered_orders_queryset(request):
    qs = Order.objects.select_related('user', 'product', 'distributor', 'delivery_store').order_by('-created_at')

    status_filter = request.query_params.get('status', '').strip().upper()
    fulfillment_filter = request.query_params.get('fulfillment_status', '').strip().upper()
    search = request.query_params.get('search', '').strip()

    if status_filter:
        qs = qs.filter(status=status_filter)
    if fulfillment_filter:
        qs = qs.filter(franchise_fulfillment_status=fulfillment_filter)
    if search:
        from django.db.models import Q
        qs = qs.filter(
            Q(reference_id__icontains=search) |
            Q(user__first_name__icontains=search) |
            Q(user__last_name__icontains=search) |
            Q(user__mobile__icontains=search) |
            Q(user__email__icontains=search) |
            Q(product__name__icontains=search)
        )

    return qs


def _order_tax_breakup(order):
    stored_amount = Decimal(str(getattr(order, 'amount', 0) or 0))
    gst_pct = Decimal(str(getattr(getattr(order, 'product', None), 'gst_percentage', 0) or 0))
    base_cost = Decimal(str(getattr(getattr(order, 'product', None), 'base_cost', 0) or 0))

    taxable_amount = base_cost if base_cost > 0 else (
        (stored_amount * Decimal('100')) / (Decimal('100') + gst_pct) if gst_pct > 0 else stored_amount
    )
    gst_amount = (taxable_amount * gst_pct / Decimal('100')) if gst_pct > 0 else Decimal('0')
    total_payable = taxable_amount + gst_amount

    return {
        'stored_amount': stored_amount.quantize(Decimal('0.01')),
        'taxable_amount': taxable_amount.quantize(Decimal('0.01')),
        'gst_pct': gst_pct.quantize(Decimal('0.001')),
        'gst_amount': gst_amount.quantize(Decimal('0.01')),
        'total_payable': total_payable.quantize(Decimal('0.01')),
    }


class AdminOrderListView(APIView):
    permission_classes = [permissions.IsAdminUser]

    @staticmethod
    def _parse_positive_int(value, default):
        try:
            parsed = int(value)
            return parsed if parsed > 0 else default
        except (TypeError, ValueError):
            return default

    def get(self, request):
        qs = _filtered_orders_queryset(request)

        # Pagination
        page_size = min(self._parse_positive_int(request.query_params.get('page_size', 50), 50), 200)
        page = self._parse_positive_int(request.query_params.get('page', 1), 1)
        total = qs.count()
        start = (page - 1) * page_size
        qs = qs[start:start + page_size]

        serializer = AdminOrderSerializer(qs, many=True)
        return Response({
            'success': True,
            'data': serializer.data,
            'count': total,
            'page': page,
            'page_size': page_size,
        })


class AdminOrderExportView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get(self, request):
        qs = _filtered_orders_queryset(request)
        stamp = timezone.now().strftime('%Y%m%d_%H%M%S')

        headers = [
            'Order Ref',
            'Customer Name',
            'Customer Mobile',
            'Customer Email',
            'Product',
            'Stored Amount',
            'Taxable Amount',
            'GST %',
            'GST Amount',
            'Total Payable (incl GST)',
            'Payment Method',
            'Order Status',
            'Fulfillment Status',
            'Created At',
        ]

        try:
            from openpyxl import Workbook

            wb = Workbook()
            ws = wb.active
            ws.title = 'Orders'
            ws.append(headers)

            for order in qs.iterator(chunk_size=500):
                tax = _order_tax_breakup(order)
                user = getattr(order, 'user', None)
                full_name = ''
                if user:
                    full_name = f"{(user.first_name or '').strip()} {(user.last_name or '').strip()}".strip() or str(user)

                ws.append([
                    order.reference_id,
                    full_name,
                    getattr(user, 'mobile', '') if user else '',
                    getattr(user, 'email', '') if user else '',
                    getattr(getattr(order, 'product', None), 'name', ''),
                    str(tax['stored_amount']),
                    str(tax['taxable_amount']),
                    str(tax['gst_pct']),
                    str(tax['gst_amount']),
                    str(tax['total_payable']),
                    order.payment_method,
                    order.status,
                    order.franchise_fulfillment_status,
                    timezone.localtime(order.created_at).strftime('%Y-%m-%d %H:%M:%S') if order.created_at else '',
                ])

            output = BytesIO()
            wb.save(output)
            output.seek(0)

            response = HttpResponse(
                output.getvalue(),
                content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            )
            response['Content-Disposition'] = f'attachment; filename="orders_export_{stamp}.xlsx"'
            return response
        except Exception:
            response = HttpResponse(content_type='text/csv')
            response['Content-Disposition'] = f'attachment; filename="orders_export_{stamp}.csv"'
            writer = csv.writer(response)
            writer.writerow(headers)

            for order in qs.iterator(chunk_size=500):
                tax = _order_tax_breakup(order)
                user = getattr(order, 'user', None)
                full_name = ''
                if user:
                    full_name = f"{(user.first_name or '').strip()} {(user.last_name or '').strip()}".strip() or str(user)

                writer.writerow([
                    order.reference_id,
                    full_name,
                    getattr(user, 'mobile', '') if user else '',
                    getattr(user, 'email', '') if user else '',
                    getattr(getattr(order, 'product', None), 'name', ''),
                    str(tax['stored_amount']),
                    str(tax['taxable_amount']),
                    str(tax['gst_pct']),
                    str(tax['gst_amount']),
                    str(tax['total_payable']),
                    order.payment_method,
                    order.status,
                    order.franchise_fulfillment_status,
                    timezone.localtime(order.created_at).strftime('%Y-%m-%d %H:%M:%S') if order.created_at else '',
                ])

            return response


class AdminOrderDetailView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get(self, request, order_id):
        order = Order.objects.select_related('user', 'product', 'distributor', 'delivery_store').filter(id=order_id).first()
        if not order:
            return error_response('Order not found', code='NOT_FOUND', status_code=404)
        return Response({'success': True, 'data': AdminOrderSerializer(order).data})


class AdminOrderFulfillmentUpdateView(APIView):
    permission_classes = [permissions.IsAdminUser]

    VALID_FULFILLMENT_STATUSES = {c[0] for c in Order.FULFILLMENT_STATUS_CHOICES}
    VALID_ORDER_STATUSES = {c[0] for c in Order.STATUS_CHOICES}

    @staticmethod
    def _reverse_order_wallet_effects(order, cancellation_reason=''):
        """Best-effort idempotent reversal of user-paid wallet/supercoin debits for an order."""
        ref = order.reference_id
        user = order.user

        wallet_refs = [
            ref,
            f'{ref}-MAIN',
            f'{ref}-REPURCHASE',
            f'{ref}-SETTLE',
        ]
        super_refs = [
            f'{ref}-SC',
            f'{ref}-SC-SETTLE',
        ]

        wallet_debits = WalletLedger.objects.filter(
            user=user,
            transaction_type=WalletLedger.TransactionType.DEBIT,
            reference_id__in=wallet_refs,
        )

        main_debit = wallet_debits.filter(wallet_type=WalletLedger.WalletType.EARNINGS).aggregate(total=models.Sum('amount'))['total'] or Decimal('0.00')
        repurchase_debit = wallet_debits.filter(wallet_type=WalletLedger.WalletType.REPURCHASE).aggregate(total=models.Sum('amount'))['total'] or Decimal('0.00')
        voucher_debit = wallet_debits.filter(wallet_type=WalletLedger.WalletType.VOUCHER).aggregate(total=models.Sum('amount'))['total'] or Decimal('0.00')

        super_debit = SuperCoinTransaction.objects.filter(
            user=user,
            transaction_type=SuperCoinTransaction.TransactionType.DEBIT,
            reference_id__in=super_refs,
        ).aggregate(total=models.Sum('amount'))['total'] or Decimal('0.00')

        main_reversed = WalletLedger.objects.filter(
            user=user,
            source_type=WalletLedger.SourceType.PURCHASE,
            reference_id=f'{ref}-REV-MAIN',
        ).exists()
        repurchase_reversed = WalletLedger.objects.filter(
            user=user,
            source_type=WalletLedger.SourceType.PURCHASE,
            reference_id=f'{ref}-REV-REPURCHASE',
        ).exists()
        voucher_reversed = WalletLedger.objects.filter(
            user=user,
            source_type=WalletLedger.SourceType.VOUCHER_ADJUSTMENT,
            reference_id=f'{ref}-REV-VOUCHER',
        ).exists()
        super_reversed = SuperCoinTransaction.objects.filter(
            user=user,
            source_type=SuperCoinTransaction.SourceType.ADJUSTMENT,
            reference_id=f'{ref}-REV-SC',
        ).exists()

        if main_debit > 0 and not main_reversed:
            WalletService.credit_main_wallet(
                user=user,
                amount=main_debit,
                source_type=WalletLedger.SourceType.PURCHASE,
                reference_id=f'{ref}-REV-MAIN',
            )

        if repurchase_debit > 0 and not repurchase_reversed:
            WalletService.credit_repurchase_wallet(
                user=user,
                amount=repurchase_debit,
                source_type=WalletLedger.SourceType.PURCHASE,
                reference_id=f'{ref}-REV-REPURCHASE',
            )

        if voucher_debit > 0 and not voucher_reversed:
            WalletService.credit_voucher_wallet(
                user=user,
                amount=voucher_debit,
                source_type=WalletLedger.SourceType.VOUCHER_ADJUSTMENT,
                reference_id=f'{ref}-REV-VOUCHER',
                remarks=(cancellation_reason or 'Order cancelled by admin'),
                metadata={'order_id': str(order.id)},
            )

        if super_debit > 0 and not super_reversed:
            WalletService.credit_supercoins(
                user=user,
                amount=super_debit,
                source_type=SuperCoinTransaction.SourceType.ADJUSTMENT,
                reference_id=f'{ref}-REV-SC',
                metadata={'order_id': str(order.id), 'reason': cancellation_reason or 'Order cancelled by admin'},
            )

    def patch(self, request, order_id):
        order = Order.objects.filter(id=order_id).first()
        if not order:
            return error_response('Order not found', code='NOT_FOUND', status_code=404)

        updated = []
        fulfillment_status = (request.data.get('fulfillment_status') or '').strip().upper()
        order_status = (request.data.get('status') or '').strip().upper()
        cancellation_reason = (request.data.get('cancellation_reason') or '').strip()

        if fulfillment_status:
            if fulfillment_status not in self.VALID_FULFILLMENT_STATUSES:
                return error_response(
                    f'Invalid fulfillment_status. Valid: {", ".join(sorted(self.VALID_FULFILLMENT_STATUSES))}',
                    code='VALIDATION_ERROR',
                    status_code=400,
                )
            if (
                order.payment_mode == 'PART'
                and order.is_availability_blocked
                and order.part_pending_amount > 0
                and fulfillment_status in {'IN_TRANSIT', 'DELIVERED', 'COLLECTED'}
            ):
                return error_response(
                    'This order is partially paid. Fulfillment is blocked until full payment is completed.',
                    code='INVALID_STATE',
                    status_code=400,
                )
            if fulfillment_status == 'CANCELLED' and not cancellation_reason:
                return error_response(
                    'Cancellation reason is required when fulfillment status is CANCELLED.',
                    code='VALIDATION_ERROR',
                    status_code=400,
                )

            order.franchise_fulfillment_status = fulfillment_status
            updated.append('franchise_fulfillment_status')

            if fulfillment_status == 'CANCELLED':
                try:
                    self._reverse_order_wallet_effects(order, cancellation_reason=cancellation_reason)
                except Exception as e:
                    return error_response(
                        f'Cancellation failed during payment reversal: {e}',
                        code='REVERSAL_FAILED',
                        status_code=400,
                    )
                order.status = 'FAILED'
                order.rejection_comment = cancellation_reason
                if 'status' not in updated:
                    updated.append('status')
                updated.append('rejection_comment')

        if order_status:
            if order_status not in self.VALID_ORDER_STATUSES:
                return error_response(
                    f'Invalid status. Valid: {", ".join(sorted(self.VALID_ORDER_STATUSES))}',
                    code='VALIDATION_ERROR',
                    status_code=400,
                )
            order.status = order_status
            updated.append('status')

        if order_status == 'COMPLETED' and is_digital_product(order.product):
            order.franchise_fulfillment_status = 'COLLECTED'
            if 'franchise_fulfillment_status' not in updated:
                updated.append('franchise_fulfillment_status')

        if not updated:
            return error_response('Provide fulfillment_status or status to update.', code='VALIDATION_ERROR', status_code=400)

        order.save(update_fields=updated + ['updated_at'])
        return Response({'success': True, 'data': AdminOrderSerializer(order).data})


class AdminOrderPaymentReviewView(APIView):
    """Approve or reject QR/bank payment review orders."""
    permission_classes = [permissions.IsAdminUser]

    def post(self, request, order_id):
        order = Order.objects.select_related('user', 'product', 'distributor').filter(id=order_id).first()
        if not order:
            return error_response('Order not found', code='NOT_FOUND', status_code=404)

        if order.status != 'PAYMENT_REVIEW':
            return error_response(
                f'Order is not pending payment review (current status: {order.status})',
                code='INVALID_STATE',
                status_code=400,
            )

        action = (request.data.get('action') or '').strip().lower()
        if action not in ('approve', 'reject'):
            return error_response("action must be 'approve' or 'reject'", code='VALIDATION_ERROR', status_code=400)

        if action == 'reject':
            comment = (request.data.get('comment') or '').strip()
            order.status = 'FAILED'
            order.rejection_comment = comment or 'Rejected by admin'
            order.reviewed_by = request.user
            order.reviewed_at = timezone.now()
            order.save(update_fields=['status', 'rejection_comment', 'reviewed_by', 'reviewed_at', 'updated_at'])
            return Response({'success': True, 'message': 'Order rejected.', 'data': AdminOrderSerializer(order).data})

        # approve
        try:
            order, distributor, _ = fulfill_pending_payment_order(order, reviewed_by=request.user)
        except Exception as e:
            return error_response(str(e), code='FULFILLMENT_FAILED', status_code=400)
        return Response({'success': True, 'message': 'Payment approved, order activated.', 'data': AdminOrderSerializer(order).data})
