from django.shortcuts import render, redirect, get_object_or_404
from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator
from django.contrib import messages
from .models import Booking, BookingItem
from services.models import Service
from category.models import Category
from garments.models import Garment
from users.models import Address
from global_settings.models import GlobalSetting
from riders.models import Rider
from brands.models import Brand
from materials.models import Material
from datetime import datetime, timedelta
import json
from decimal import Decimal, InvalidOperation
from django.core.serializers.json import DjangoJSONEncoder
from django.http import JsonResponse
from django.db.models import Q

class BookingManagementView(LoginRequiredMixin, View):
	def get(self, request):
		qs = Booking.objects.all().select_related('user')

		# filters
		q = request.GET.get('q')
		status = request.GET.get('status')
		pickup_type = request.GET.get('pickup_type')
		date_from = request.GET.get('date_from')
		date_to = request.GET.get('date_to')

		if q:
			qs = qs.filter(booking_id__icontains=q) | qs.filter(contact_mobile__icontains=q)
		if status:
			qs = qs.filter(booking_status=status)
		if pickup_type:
			qs = qs.filter(pickup_type=pickup_type)
		if date_from:
			try:
				df = datetime.strptime(date_from, '%Y-%m-%d').date()
				qs = qs.filter(created_at__date__gte=df)
			except Exception:
				pass
		if date_to:
			try:
				dt = datetime.strptime(date_to, '%Y-%m-%d').date()
				qs = qs.filter(created_at__date__lte=dt)
			except Exception:
				pass

		qs = qs.order_by('-created_at')
		paginator = Paginator(qs, 25)
		page_number = request.GET.get('page')
		page_obj = paginator.get_page(page_number)

		context = {
			'bookings': page_obj,
			'page_obj': page_obj,
			'query': q or '',
			'status': status or '',
			'pickup_type': pickup_type or '',
			'date_from': date_from or '',
			'date_to': date_to or '',
		}
		return render(request, 'pages/bookings/booking_management.html', context)


class BookingEditView(LoginRequiredMixin, View):
	def get(self, request, pk):
		booking = get_object_or_404(Booking, pk=pk)
		
		# Fetch related data for dropdowns
		services = Service.objects.filter(status='active')
		categories = Category.objects.filter(status='active', trash=False)
		garments = Garment.objects.filter(status='active', trash=False).select_related('category')
		user_addresses = Address.objects.filter(user=booking.user, trash=False)
		global_setting = GlobalSetting.objects.first()
		time_slots = global_setting.time_slots if global_setting else []
		riders = Rider.objects.filter(status='active', trash=False)
		brands = Brand.objects.filter(trash=False, status='active')
		materials = Material.objects.filter(trash=False, status='active')

		# Serialize complex data for JS usage
		# We need garment prices which are in a JSON field, so we'll build a custom list
		garments_data = []
		for g in garments:
			garments_data.append({
				'id': g.id,
				'name': g.name,
				'category_id': g.category_id if g.category else None,
				'service_prices': g.service_prices
			})
		
		# Serialize addresses for JS
		addresses_data = []
		for addr in user_addresses:
			addresses_data.append({
				'id': addr.id,
				'address_type': addr.address_type,
				'room_no': addr.room_no,
				'street_address': addr.street_address,
				'landmark': addr.landmark,
				'city': addr.city,
				'state': addr.state,
				'zip_code': addr.zip_code,
				'full_text': f"{addr.get_address_type_display()}: {addr.street_address}, {addr.city}"
			})

		# Serialize BookingItems
		booking_items_data = {}
		for item in booking.items.all():
			gid = item.garment_id
			if gid not in booking_items_data:
				booking_items_data[gid] = []
			booking_items_data[gid].append({
				'id': item.id,
				'tag': item.tag,
				'color': item.color,
				'is_damaged': item.is_damaged,
				'damage_description': item.damage_description,
				'item_type': item.item_type
			})

		pickup_address_json = json.dumps(booking.pickup_address, cls=DjangoJSONEncoder)
		order_details_json = json.dumps(booking.order_details, cls=DjangoJSONEncoder)
		premium_details_json = json.dumps(booking.premium_details, cls=DjangoJSONEncoder)
		luxury_details_json = json.dumps(booking.luxury_details, cls=DjangoJSONEncoder)
		garments_json = json.dumps(garments_data, cls=DjangoJSONEncoder)
		addresses_json = json.dumps(addresses_data, cls=DjangoJSONEncoder)
		booking_items_json = json.dumps(booking_items_data, cls=DjangoJSONEncoder)
		brands_json = json.dumps(list(brands.values('id', 'brand_name')), cls=DjangoJSONEncoder)
		materials_json = json.dumps(list(materials.values('id', 'material_name')), cls=DjangoJSONEncoder)

		context = {
			'booking': booking,
			'services': services,
			'categories': categories,
			'garments': garments, # Queryset for template loop if needed
			'user_addresses': user_addresses,
			'time_slots': time_slots,
			'riders': riders,
			'brands': brands,
			'materials': materials,
			
			# JSON data for JS
			'pickup_address_json': pickup_address_json,
			'order_details_json': order_details_json,
			'premium_details_json': premium_details_json,
			'luxury_details_json': luxury_details_json,
			'garments_json': garments_json,
			'addresses_json': addresses_json,
			'booking_items_json': booking_items_json,
			'brands_json': brands_json,
			'materials_json': materials_json,
		}
		return render(request, 'pages/bookings/booking_edit.html', context)

	def post(self, request, pk):
		booking = get_object_or_404(Booking, pk=pk)
		# allow editing a few fields
		booking.booking_status = request.POST.get('booking_status', booking.booking_status)
		booking.pickup_type = request.POST.get('pickup_type', booking.pickup_type)
		booking.schedule_pickup_date = request.POST.get('schedule_pickup_date') or booking.schedule_pickup_date
		booking.schedule_pickup_time = request.POST.get('schedule_pickup_time') or booking.schedule_pickup_time
		booking.contact_name = request.POST.get('contact_name', booking.contact_name)
		booking.contact_mobile = request.POST.get('contact_mobile', booking.contact_mobile)
		booking.pickup_instructions = request.POST.get('pickup_instructions', booking.pickup_instructions)

		# rider assignment
		rider_id = request.POST.get('rider')
		if rider_id:
			try:
				booking.rider = Rider.objects.get(pk=rider_id)
			except Rider.DoesNotExist:
				pass
		else:
			booking.rider = None

		# pickup_address: accept JSON or plain text
		pickup_address_raw = request.POST.get('pickup_address')
		if pickup_address_raw is not None:
			pickup_addr = None
			try:
				# try parse JSON
				pickup_addr = json.loads(pickup_address_raw)
			except Exception:
				# store as simple text under a key to keep JSONField consistent
				pickup_addr = {"raw_address": pickup_address_raw}
			booking.pickup_address = pickup_addr

		# total_quantity
		total_qty_raw = request.POST.get('total_quantity')
		if total_qty_raw is not None and total_qty_raw != '':
			try:
				booking.total_quantity = int(total_qty_raw)
			except (ValueError, TypeError):
				messages.warning(request, 'Invalid total quantity provided; keeping previous value.')

		# total_final_price
		total_price_raw = request.POST.get('total_final_price')
		if total_price_raw is not None and total_price_raw != '':
			try:
				booking.total_final_price = Decimal(total_price_raw)
			except (InvalidOperation, ValueError):
				messages.warning(request, 'Invalid total price provided; keeping previous value.')

		# order_details
		order_details_raw = request.POST.get('order_details')
		if order_details_raw:
			try:
				booking.order_details = json.loads(order_details_raw)
			except json.JSONDecodeError:
				messages.warning(request, 'Invalid JSON for order details; keeping previous value.')

		# premium_details
		premium_details_raw = request.POST.get('premium_details')
		if premium_details_raw:
			try:
				booking.premium_details = json.loads(premium_details_raw)
			except json.JSONDecodeError:
				messages.warning(request, 'Invalid JSON for premium details; keeping previous value.')

		# luxury_details
		luxury_details_raw = request.POST.get('luxury_details')
		if luxury_details_raw:
			try:
				booking.luxury_details = json.loads(luxury_details_raw)
			except json.JSONDecodeError:
				messages.warning(request, 'Invalid JSON for luxury details; keeping previous value.')

		# Generate tags if requested or always ensure they exist
		if 'generate_tags' in request.POST:
			booking.generate_item_tags()
			booking.save()
			messages.success(request, 'Item tags generated successfully. You can now inspect items.')
			return redirect('booking-edit', pk=booking.pk)

		booking.save()
		messages.success(request, 'Booking updated successfully!')
		return redirect('booking-management')


class BookingDetailView(LoginRequiredMixin, View):
	def get(self, request, pk):
		booking = get_object_or_404(Booking, pk=pk)
		
		# Inject items into details for display
		import copy
		order_details_display = copy.deepcopy(booking.order_details) if booking.order_details else {}
		premium_details_display = copy.deepcopy(booking.premium_details) if booking.premium_details else {}
		luxury_details_display = copy.deepcopy(booking.luxury_details) if booking.luxury_details else {}
		
		# Fetch all items once
		all_items = list(booking.items.all())
		
		def inject_items(details_dict, item_type):
			if details_dict:
				for service_name, categories in details_dict.items():
					for category_name, items in categories.items():
						if isinstance(items, list):
							for item in items:
								gid = item.get('item_id')
								try:
									gid = int(gid)
								except (ValueError, TypeError):
									pass
									
								# Filter items for this garment and type
								item['booking_items'] = [
									bi for bi in all_items 
									if bi.garment_id == gid and bi.item_type == item_type
								]

		inject_items(order_details_display, 'standard')
		inject_items(premium_details_display, 'premium')
		inject_items(luxury_details_display, 'luxury')

		return render(request, 'pages/bookings/booking_detail.html', {
			'booking': booking,
			'order_details_display': order_details_display,
			'premium_details_display': premium_details_display,
			'luxury_details_display': luxury_details_display,
		})


class BookingItemUpdateView(LoginRequiredMixin, View):
    def post(self, request, pk):
        item = get_object_or_404(BookingItem, pk=pk)
        
        try:
            data = json.loads(request.body)
            item.color = data.get('color', item.color)
            item.is_damaged = data.get('is_damaged', item.is_damaged)
            item.damage_description = data.get('damage_description', item.damage_description)
            item.save()
            return JsonResponse({'status': 'success'})
        except Exception as e:
            return JsonResponse({'status': 'error', 'message': str(e)}, status=400)

class BookingDeleteView(LoginRequiredMixin, View):
    def post(self, request, pk):
        booking = get_object_or_404(Booking, pk=pk)
        booking.delete()
        messages.success(request, 'Booking deleted successfully!')
        return redirect('booking-management')

class BookingItemDeleteView(LoginRequiredMixin, View):
    def post(self, request, pk):
        item = get_object_or_404(BookingItem, pk=pk)
        item.delete()
        return JsonResponse({'status': 'success'})

class TagListView(LoginRequiredMixin, View):
    def get(self, request):
        # Fetch all bookings for client-side datatable
        bookings = Booking.objects.select_related('user').order_by('-created_at')
        
        context = {
            'bookings': bookings,
        }
        return render(request, 'pages/bookings/tag_list.html', context)

class BookingTagsView(LoginRequiredMixin, View):
    def get(self, request, pk):
        booking = get_object_or_404(Booking, pk=pk)
        tags = booking.items.select_related('garment', 'service', 'category').order_by('tag')
        
        status_filter = request.GET.get('status')
        if status_filter == 'ok':
            tags = tags.filter(is_damaged=False)
        elif status_filter == 'damaged':
            tags = tags.filter(is_damaged=True)

        delivery_date = booking.created_at.date() + timedelta(days=5)

        context = {
            'booking': booking,
            'tags': tags,
            'status_filter': status_filter,
            'delivery_date': delivery_date,
            'order_details': booking.order_details,
            'premium_details': booking.premium_details,
            'luxury_details': booking.luxury_details,
        }
        return render(request, 'pages/bookings/booking_tags.html', context)

class BookingTagPrintView(LoginRequiredMixin, View):
    def get(self, request, pk):
        booking = get_object_or_404(Booking, pk=pk)
        items = booking.items.all().order_by('id')
        
        # Calculate totals per garment type
        garment_totals = {}
        for item in items:
            g_id = item.garment.id
            garment_totals[g_id] = garment_totals.get(g_id, 0) + 1
            
        # Prepare list of items with their index
        print_items = []
        total_items = items.count()
        garment_counters = {}
        
        for index, item in enumerate(items, start=1):
            g_id = item.garment.id
            garment_counters[g_id] = garment_counters.get(g_id, 0) + 1
            
            print_items.append({
                'item': item,
                'index': index,
                'specific_index': garment_counters[g_id],
                'specific_total': garment_totals[g_id],
            })
            
        # Delivery date logic (same as elsewhere: created_at + 5 days)
        delivery_date = booking.created_at.date() + timedelta(days=5)
        
        context = {
            'booking': booking,
            'print_items': print_items,
            'total_items': total_items,
            'delivery_date': delivery_date,
        }
        return render(request, 'pages/bookings/print_tag.html', context)
