/**
 * Booking Service
 * Business logic for booking/order operations
 */

const mongoose = require('mongoose');
const Booking = require('../models/booking.model');
const Driver = require('../models/driver.model');
const Vehicle = require('../models/vehicle.model');
const Vendor = require('../models/vendor.model');
const Zone = require('../models/zone.model');
const VehicleGroup = require('../models/vehicle-group.model');
const VehicleModel = require('../models/vehicle-model.model');
const VehicleCategory = require('../models/vehicle-category.model');
const combineDateAndTime = require('../helper/combineDateAndTime');
const timelineService = require('./timeline.service');
const vehicleService = require('./vehicle.service');
const { sendOtp } = require('../utils/sendOtp');
const Setting = require('../models/setting.model');
const webhookService = require('./webhook.service');

/**
 * Check if a point is inside a polygon
 */
const isPointInPolygon = (point, polygon) => {
    // Validate polygon has at least 3 points to form a shape
    if (!polygon || polygon.length < 3) return false;

    // Convert point coordinates to numbers (in case they're strings from query params)
    const x = parseFloat(point.lng);
    const y = parseFloat(point.lat);

    // Check for invalid coordinates
    if (isNaN(x) || isNaN(y)) return false;

    let inside = false;

    // Ray-casting algorithm: cast a ray from point to infinity and count intersections
    for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
        const xi = parseFloat(polygon[i].lng);
        const yi = parseFloat(polygon[i].lat);
        const xj = parseFloat(polygon[j].lng);
        const yj = parseFloat(polygon[j].lat);

        // Skip invalid polygon coordinates
        if (isNaN(xi) || isNaN(yi) || isNaN(xj) || isNaN(yj)) continue;

        // Check if the ray intersects with the polygon edge
        const intersect =
            ((yi > y) !== (yj > y)) &&
            (x < ((xj - xi) * (y - yi)) / (yj - yi) + xi);

        if (intersect) inside = !inside;
    }

    return inside;
};
exports.isPointInPolygon = isPointInPolygon;

// ==========================================
// QUERY SERVICES
// ==========================================

/**
 * Get orders with filters and pagination
 * @param {Object} filters - zoneId, vendorId, driverId, status
 * @param {Object} pagination - page, limit
 */
exports.getOrdersWithFilters = async (filters = {}, pagination = {}) => {
    const { zoneId, vendorId, driverId, status } = filters;
    const { page = 1, limit = 10 } = pagination;
    const skip = (page - 1) * limit;

    const query = {};
    if (zoneId) query.zone = zoneId;
    if (vendorId) query.vendor = vendorId;
    if (driverId) query.driver = driverId;
    if (status && status !== 'all') query.status = status;

    const [list, total] = await Promise.all([
        Booking.find(query)
            .populate('customer', 'fullName phone')
            .populate('driver', 'fullName phone')
            .populate('vendor', 'vendorName')
            .select('orderId status pickupLocation dropLocation fare tripType createdAt')
            .skip(skip).limit(limit).sort({ createdAt: -1 }),
        Booking.countDocuments(query)
    ]);

    return { list, total, pages: Math.ceil(total / limit), page };
};

/**
 * Get order statistics
 * @param {String} zoneId - Optional zone filter
 */
exports.getOrderStats = async (zoneId = null) => {
    const match = {};
    if (zoneId) match.zone = new mongoose.Types.ObjectId(zoneId);

    const stats = await Booking.aggregate([
        { $match: match },
        {
            $group: {
                _id: null,
                total: { $sum: 1 },
                revenue: { $sum: '$fare.total' },
                completed: { $sum: { $cond: [{ $eq: ['$status', 'completed'] }, 1, 0] } },
                cancelled: { $sum: { $cond: [{ $eq: ['$status', 'cancelled'] }, 1, 0] } },
                active: { $sum: { $cond: [{ $in: ['$status', ['created', 'assigned', 'arrived', 'inProgress']] }, 1, 0] } }
            }
        }
    ]);

    const result = stats[0] || { total: 0, revenue: 0, completed: 0, cancelled: 0, active: 0 };
    result.revenue = Math.round(result.revenue || 0);
    return result;
};

/**
 * Get ongoing/active orders
 * @param {String} zoneId - Optional zone filter
 * @param {Number} limit - Max orders to return
 */
exports.getOngoingOrders = async (zoneId = null, limit = 10) => {
    const query = { status: { $in: ['created', 'assigned', 'arrived', 'inProgress'] } };
    if (zoneId) query.zone = zoneId;

    return Booking.find(query)
        .populate('customer', 'fullName phone')
        .populate('driver', 'fullName phone')
        .select('orderId status pickupLocation dropLocation fare.total createdAt')
        .sort({ createdAt: -1 })
        .limit(limit);
};

/**
 * Get bookings with full filtering
 * @param {Object} query - MongoDB query
 * @param {Object} pagination - page, limit
 */
exports.getAllBookings = async (query, pagination) => {
    const { page = 1, limit = 10 } = pagination;
    const skip = (page - 1) * limit;

    const [bookings, total] = await Promise.all([
        Booking.find(query)
            .populate('customer', 'fullName email phone')
            .populate('driver', 'fullName phone vehicle')
            .populate('zone', 'name')
            .sort({ createdAt: -1 })
            .skip(skip)
            .limit(parseInt(limit)),
        Booking.countDocuments(query)
    ]);

    return { bookings, total, pages: Math.ceil(total / limit), currentPage: parseInt(page) };
};

/**
 * Get booking by ID
 * @param {String} bookingId - Booking ID
 */
exports.getBookingById = async (bookingId) => {
    const result = await Booking.aggregate([
        { $match: { _id: new mongoose.Types.ObjectId(bookingId) } },
        // Ensure vehicle field is an ObjectId for lookup
        {
            $addFields: {
                vehicle: { $toObjectId: "$vehicle" },
                customer: { $toObjectId: "$customer" },
                driver: { $toObjectId: "$driver" },
                vendor: { $toObjectId: "$vendor" },
                zone: { $toObjectId: "$zone" }
            }
        },
        // Lookup customer
        {
            $lookup: {
                from: 'users',
                localField: 'customer',
                foreignField: '_id',
                as: 'customer',
                pipeline: [{ $project: { fullName: 1, email: 1, phone: 1 } }]
            }
        },
        { $unwind: { path: '$customer', preserveNullAndEmptyArrays: true } },
        // Lookup driver
        {
            $lookup: {
                from: 'drivers',
                localField: 'driver',
                foreignField: '_id',
                as: 'driver',
                pipeline: [{ $project: { fullName: 1, phone: 1 } }]
            }
        },
        { $unwind: { path: '$driver', preserveNullAndEmptyArrays: true } },
        // Lookup vehicle with separate model, group, brand, and category lookups
        {
            $lookup: {
                from: 'vehicles',
                localField: 'vehicle',
                foreignField: '_id',
                as: 'vehicle',
                pipeline: [
                    { $project: { registrationNumber: 1, model: 1, group: 1, brand: 1, category: 1, color: 1, status: 1, mileage: 1 } },
                    // Lookup model
                    {
                        $lookup: {
                            from: 'vehiclemodels',
                            localField: 'model',
                            foreignField: '_id',
                            as: 'model',
                            pipeline: [{ $project: { modelName: 1 } }]
                        }
                    },
                    { $unwind: { path: '$model', preserveNullAndEmptyArrays: true } },
                    // Lookup group
                    {
                        $lookup: {
                            from: 'vehiclegroups',
                            localField: 'group',
                            foreignField: '_id',
                            as: 'group',
                            pipeline: [{ $project: { groupName: 1, tier: 1, seats: 1 } }]
                        }
                    },
                    { $unwind: { path: '$group', preserveNullAndEmptyArrays: true } },
                    // Lookup brand
                    {
                        $lookup: {
                            from: 'vehiclebrands',
                            localField: 'brand',
                            foreignField: '_id',
                            as: 'brand',
                            pipeline: [{ $project: { brandName: 1, logo: 1 } }]
                        }
                    },
                ]
            }
        },
        { $unwind: { path: '$vehicle', preserveNullAndEmptyArrays: true } },
        // Lookup vendor
        {
            $lookup: {
                from: 'vendors',
                localField: 'vendor',
                foreignField: '_id',
                as: 'vendor',
                pipeline: [{ $project: { vendorName: 1 } }]
            }
        },
        { $unwind: { path: '$vendor', preserveNullAndEmptyArrays: true } },
        // Lookup zone
        {
            $lookup: {
                from: 'zones',
                localField: 'zone',
                foreignField: '_id',
                as: 'zone',
                pipeline: [{ $project: { zoneName: 1 } }]
            }
        },
        { $unwind: { path: '$zone', preserveNullAndEmptyArrays: true } },
        // Lookup timeline
        {
            $lookup: {
                from: 'timelines',
                localField: '_id',
                foreignField: 'referenceId',
                as: 'timeline',
                pipeline: [{ $sort: { timestamp: -1 } }]
            }
        }
    ]);

    if (!result || result.length === 0) {
        throw { message: 'Booking not found', statusCode: 404 };
    }
    // console.log(result)

    return result[0];
};

exports.getBookingsByUserId = async (userId) => {
    const result = await Booking.aggregate([
        { $match: { customer: new mongoose.Types.ObjectId(userId) } },
        // Lookup driver - name only
        {
            $lookup: {
                from: 'drivers',
                localField: 'driver',
                foreignField: '_id',
                as: 'driver',
                pipeline: [{ $project: { fullName: 1 } }]
            }
        },
        { $unwind: { path: '$driver', preserveNullAndEmptyArrays: true } },
        // Sort by newest first
        { $sort: { createdAt: -1 } }
    ]);

    return result;
};

// ==========================================
// MUTATION SERVICES
// ==========================================

/**
 * Generate unique order ID
 * Format: TRP + Month(MM) + Year(YY) + Count(XXXX)
 */
exports.generateOrderId = async () => {
    const date = new Date();
    const month = (date.getMonth() + 1).toString().padStart(2, '0');
    const year = date.getFullYear().toString().slice(-2);

    // Get count for today to make it more sequential or just total count?
    // User asked for "count of trip", usually means sequence.
    const count = await Booking.countDocuments();
    const sequence = (count + 1).toString().padStart(4, '0');

    let orderId = `TRP${month}${year}${sequence}`;

    // Check for collision and increment if necessary
    let isExist = await Booking.findOne({ orderId });
    let increment = 1;
    while (isExist) {
        const nextSequence = (count + 1 + increment).toString().padStart(4, '0');
        orderId = `TRP${month}${year}${nextSequence}`;
        isExist = await Booking.findOne({ orderId });
        increment++;
    }

    return orderId;
};

/**
 * Calculate distance and duration using Google Maps Distance Matrix API
 * @param {number} lat1 - Origin latitude
 * @param {number} lon1 - Origin longitude
 * @param {number} lat2 - Destination latitude
 * @param {number} lon2 - Destination longitude
 * @returns {Promise<{distance: number, duration: number}>}
 */
exports.calculateDistanceAsync = async (lat1, lon1, lat2, lon2) => {
    const mapService = require('./map.service');
    const result = await mapService.getDistanceAndDuration(
        { lat: lat1, lng: lon1 },
        { lat: lat2, lng: lon2 }
    );
    // console.log("result", result)
    if (result.distance > 0) {
        console.log(`Google Maps Distance: ${result.distance} km, Duration: ${result.duration} mins`);
        return result;
    }

    // Throw error if Google Maps returns 0 distance (no fallback)
    throw new Error('Unable to calculate distance using Google Maps API');
};

// validate and format stops
exports.validateStops = (stops) => {
    if (!stops || !Array.isArray(stops)) return [];
    if (stops.length > 5) {
        throw { message: 'Maximum 5 intermediate stops allowed per booking', statusCode: 400 };
    }

    const validatedStops = [];
    for (let i = 0; i < stops.length; i++) {
        const stop = stops[i];
        if (!stop.address) {
            throw { message: `Stop ${i + 1} is missing required address field`, statusCode: 400 };
        }
        validatedStops.push({
            placeId: stop.placeId || stop.place_id || null,
            address: stop.address,
            description: stop.description || stop.address,
            region: stop.region || stop.city || null,
            coordinates: stop.coordinates ||
                (stop.longitude && stop.latitude ? [stop.longitude, stop.latitude] : []),
            pincode: stop.pincode || null,
            googleCity: stop.googleCity || stop.google_city || null,
            isCity: stop.isCity !== undefined ? stop.isCity : (stop.is_city !== undefined ? stop.is_city : true),
            isAirport: stop.isAirport !== undefined ? stop.isAirport : (stop.is_airport !== undefined ? stop.is_airport : false),
            stopOrder: stop.stopOrder || (i + 1),
            waitTime: stop.waitTime || 0
        });
    }
    return validatedStops.sort((a, b) => a.stopOrder - b.stopOrder);
};

/**
 * Check if a given time falls within a night range
 * @param {Date} time - The time to check
 * @param {String} start - Start time in "HH:mm" format
 * @param {String} end - End time in "HH:mm" format
 */
const isNightTime = (time, start, end) => {
    if (!time || !start || !end) return false;
    const date = new Date(time);
    const hours = date.getHours();
    const minutes = date.getMinutes();
    const currentTime = hours * 60 + minutes;

    const [startH, startM] = start.split(':').map(Number);
    const startTime = startH * 60 + startM;

    const [endH, endM] = end.split(':').map(Number);
    const endTime = endH * 60 + endM;

    if (startTime > endTime) {
        // Night shift crosses midnight (e.g., 22:00 to 06:00)
        return currentTime >= startTime || currentTime <= endTime;
    } else {
        // Night shift within the same day (e.g., 00:00 to 05:00)
        return currentTime >= startTime && currentTime <= endTime;
    }
};

/**
 * Calculate fare based on trip type, distance, fuel pricing, night charges, and zone rules
 */
exports.calculateFare = (tripType, {
    distance,
    stopsCount,
    totalWaitTime = 0,
    rentalPackage,
    group,
    zone,
    fuelType,
    isAc,
    scheduledTime
}) => {
    let baseFare = 0;
    let distanceFare = 0;
    let stopsFare = 0;
    let totalFare = 0;
    let ratePerKm = 0;
    // 1. Get Base Fare and Rate per KM from fuelPricing
    // Default to first pricing if fuelType not matched
    const pricing = group?.fuelPricing?.find(p => p.fuelType === fuelType) || group?.fuelPricing?.[0];

    if (pricing) {
        baseFare = isAc ? (pricing.acBaseFare || 0) : (pricing.nonAcBaseFare || 0);
        ratePerKm = isAc ? (pricing.ratePerKmAc || 0) : (pricing.ratePerKmNonAc || 0);
        const baseKm = pricing.baseKm || 0;

        // Distance Fare
        if (distance > baseKm) {
            distanceFare = (distance - baseKm) * ratePerKm;
        }

        // Ensure minimum fare if applicable
        if (pricing.minFare && (baseFare + distanceFare) < pricing.minFare) {
            distanceFare = pricing.minFare - baseFare;
        }
    }
    // else {
    //     // Fallback for missing pricing (old behavior or default)
    //     baseFare = zone?.baseFare || 50;
    //     distanceFare = distance * 12;
    // }

    // 2. Stops Fare (Waiting Charge only, no fixed base fare per stop)
    if (totalWaitTime > 0) {
        const waitingChargeRate = pricing?.waitingCharge || 0;
        stopsFare = totalWaitTime * waitingChargeRate;
    }

    // 3. Rental Package logic - Use rentalPricing if available
    if (tripType === 'rental' && rentalPackage) {
        let rentalOption = group?.rentalPricing?.find(rp =>
            rp.hours === rentalPackage.hours &&
            (rp.fuelType === fuelType || !rp.fuelType || rp.fuelType === 'petrol')
        );

        if (!rentalOption) {
            rentalOption = group?.rentalPricing?.find(rp => rp.hours === rentalPackage.hours);
        }

        if (rentalOption) {
            // Use defined rental pricing
            baseFare = rentalOption.baseFare;
            const ratePerKm = rentalOption.extraFarePerKm;
            const baseKm = rentalOption.baseKm;

            // Calculate extra distance charges
            distanceFare = 0;
            if (distance > baseKm) {
                distanceFare = (distance - baseKm) * ratePerKm;
            }

            // Reset totalFare for rental calculation
            totalFare = baseFare + distanceFare + stopsFare;
        } else {
            totalFare = baseFare + distanceFare + stopsFare;
        }
    } else {
        totalFare = baseFare + distanceFare + stopsFare;
    }

    // 4. Trip Type Multipliers
    if (tripType === 'roundTrip') {
        totalFare *= 1.8;
    }

    let airportCharge = 0;
    if (tripType === 'airport') {
        airportCharge = 100;
        totalFare += airportCharge;
    }

    // 5. Night Charge
    let nightCharge = 0;
    if (isNightTime(scheduledTime, group?.nightStartTime, group?.nightEndTime)) {
        if (group?.nightChargeType === 'percentage') {
            nightCharge = (totalFare * (group.nightChargeValue || 0)) / 100;
        } else {
            nightCharge = (group?.nightChargeValue || 0);
        }
        totalFare += nightCharge;
    }

    // 6. Extra Fare Rule (Zone)
    let zoneExtraCharge = 0;
    if (zone?.extraFareRule && zone.extraFareRule.fareAmount) {
        const rule = zone.extraFareRule;
        if (rule.fareType === 'percentage') {
            zoneExtraCharge = (totalFare * rule.fareAmount) / 100;
        } else {
            zoneExtraCharge = rule.fareAmount;
        }
        totalFare += zoneExtraCharge;
    }

    // 7. Taxes and Discounts (Set to 0 as requested)
    const taxes = 0;
    const discount = 0;
    totalFare = totalFare + taxes - discount;

    return {
        base: Math.round(baseFare),
        distance: Math.round(distanceFare),
        stops: Math.round(stopsFare),
        nightCharge: Math.round(nightCharge),
        airportCharge: Math.round(airportCharge),
        zoneExtraCharge: Math.round(zoneExtraCharge),
        total: Math.round(totalFare),
        taxes,
        discount,
        ratePerKm
    };
};

/**
 * Create a new booking
 * @param {Object} bookingData - Booking details (now includes groupId/modelId instead of vehicleId)
 * @param {String} customerId - Customer user ID
 */
exports.createBooking = async (bookingData, customerId) => {
    const {
        pickupLocation, dropLocation, stops,
        groupId, modelId,  // NEW: Group and Model IDs from search results
        vehicleId,         // OPTIONAL: For backward compatibility
        tripType,
        rentalPackage, pickupDate, pickupTime, returnDate, returnTime,
        distance, duration, paymentMode, zoneId,
        travellerDetails   // NEW: Traveller details from frontend
    } = bookingData;

    // Validate required fields - now requires groupId and modelId (not vehicleId)
    if (!pickupLocation || !tripType || !pickupDate) {
        throw { message: 'Missing required fields: Pickup Location, Trip Type, and Pickup Date are mandatory.', statusCode: 400 };
    }

    // Extract groupId and modelId from vehicleId if not provided (backward compatibility)
    let finalGroupId = groupId;
    let finalModelId = modelId;

    if ((!finalGroupId || !finalModelId) && vehicleId) {
        // If vehicleId is provided but groupId/modelId are missing, fetch them from the vehicle
        const vehicle = await Vehicle.findById(vehicleId).select('group model').lean();
        if (vehicle) {
            finalGroupId = finalGroupId || vehicle.group;
            finalModelId = finalModelId || vehicle.model;
        }
    }

    if (!finalGroupId || !finalModelId) {
        throw { message: 'Missing required fields: Group ID and Model ID are mandatory.', statusCode: 400 };
    }

    // Validate stops
    const validatedStops = exports.validateStops(stops);

    // Combine Pickup Date & Time
    const scheduledTime = combineDateAndTime(pickupDate, pickupTime);
    if (!scheduledTime || isNaN(scheduledTime.getTime())) {
        throw { message: 'Invalid Pickup Date/Time format', statusCode: 400 };
    }

    // Type-specific validation
    let finalDropLocation = dropLocation;
    let finalReturnDate = null;

    switch (tripType) {
        case 'oneWay':
        case 'airport':
            if (!dropLocation) throw { message: 'Drop Location is required for oneWay/airport trips', statusCode: 400 };
            if (!distance || !duration) throw { message: 'Distance and Duration are required', statusCode: 400 };
            break;
        case 'roundTrip':
            if (!dropLocation) throw { message: 'Drop Location is required for roundTrip', statusCode: 400 };
            if (!returnDate) throw { message: 'Return Date is required for roundTrip', statusCode: 400 };
            finalReturnDate = combineDateAndTime(returnDate, returnTime);
            if (finalReturnDate <= scheduledTime) throw { message: 'Return Date must be after Pickup Date', statusCode: 400 };
            break;
        case 'rental':
            if (!rentalPackage || !rentalPackage.hours || !rentalPackage.distance) {
                throw { message: 'Rental Package details (hours, distance) are required', statusCode: 400 };
            }
            if (!finalDropLocation) finalDropLocation = pickupLocation;
            break;
        default:
            throw { message: 'Invalid Trip Type', statusCode: 400 };
    }

    // Fetch Group and Model for fare calculation
    const group = await VehicleGroup.findById(finalGroupId)
        .populate('category', 'categoryName defaultSeats');

    if (!group) {
        throw { message: 'Vehicle group not found', statusCode: 404 };
    }

    const model = await VehicleModel.findById(finalModelId)
        .populate('brandName', 'brandName');

    if (!model) {
        throw { message: 'Vehicle model not found', statusCode: 404 };
    }

    // Determine zone - either from zoneId param or from pickup coordinates
    let zone;
    if (zoneId) {
        zone = await Zone.findById(zoneId);
    } else if (pickupLocation?.coordinates) {
        // Find zone based on pickup coordinates
        const zones = await Zone.find({ isActive: true }).lean();
        const pickupPoint = {
            lat: pickupLocation.coordinates[1],
            lng: pickupLocation.coordinates[0]
        };
        zone = zones.find(z => isPointInPolygon(pickupPoint, z.coordinates));
    }

    if (!zone || !zone.isActive) {
        throw { message: 'No service available in this area', statusCode: 404 };
    }

    // Find best vendor using smart assignment (least active bookings)
    const vendorResult = await exports.findBestVendorForBooking(zone._id, finalGroupId, finalModelId);
    const vendor = vendorResult.vendor;

    // Calculate fare using group pricing
    const totalWaitTime = validatedStops.reduce((sum, stop) => sum + (stop.waitTime || 0), 0);
    const fare = exports.calculateFare(tripType, {
        distance: distance || 0,
        stopsCount: validatedStops.length,
        rentalPackage,
        totalWaitTime,
        group,
        zone,
        fuelType: bookingData.fuelType || 'petrol',
        isAc: bookingData.isAc !== undefined ? bookingData.isAc : (model?.acAvailable ?? true),
        scheduledTime: scheduledTime
    });

    // Apply Group Discount
    if (group.discount) {
        const discountResult = vehicleService.calculateDiscountAmount(fare.total, group.discount);
        const discountAmount = discountResult.discountAmount || 0;
        fare.discount = discountAmount;
        fare.total = Math.max(0, fare.total - discountAmount);
    }

    // Generate Order ID
    const orderId = await exports.generateOrderId();

    // Build display name for timeline
    const brandName = model.brandName?.brandName || '';
    const modelName = model.modelName || group.groupName || 'Vehicle';
    const displayName = brandName ? `${brandName} ${modelName}` : modelName;

    // Create Booking - vehicle is optional (assigned by vendor later)
    const booking = new Booking({
        orderId,
        customer: customerId,
        zone: zone._id,
        vendor: vendor._id,
        pickupLocation,
        dropLocation: finalDropLocation,
        stops: validatedStops,
        vehicle: vehicleId || undefined,  // Optional - assigned by vendor
        vehicleGroup: finalGroupId,             // NEW: What customer selected
        vehicleModel: finalModelId,             // NEW: Specific model
        tripType,
        rentalPackage: tripType === 'rental' ? rentalPackage : undefined,
        returnDate: finalReturnDate,
        pickupDate: pickupDate || scheduledTime,
        pickupTime,
        bookingMode: 'scheduled',
        scheduledTime,
        distance: distance || 0,
        duration: duration || 0,
        fare,
        payment: { mode: paymentMode || 'cash', status: 'pending' },
        status: (paymentMode === 'online' || paymentMode === 'wallet') ? 'payment_pending' : 'created',
        travellerDetails: travellerDetails || {}  // NEW: Add traveller details
    });

    await booking.save();


    await timelineService.addTimelineEntry(booking._id, {
        title: 'Order Placed',
        status: 'created',
        description: `Booking created for ${booking.vehicleModel?.modelName || 'vehicle'}. Assigned to vendor. Awaiting vehicle and driver assignment.`
    });

    // Trigger Webhook for Booking Confirmation
    webhookService.triggerWebhook('booking_confirmation', booking);

    return booking;
};

/**
 * Assign driver and/or vehicle to booking
 * @param {String} bookingId - Booking ID
 * @param {String} driverId - Driver ID (optional if vehicle has assignedDriver)
 * @param {String} vehicleId - Vehicle ID (optional)
 */
exports.assignDriver = async (bookingId, driverId, vehicleId = null) => {
    const booking = await Booking.findById(bookingId)
        .populate('driver', 'fullName phone')
        .populate('customer', 'fullName phone')
        .populate('vehicleGroup', 'groupName')
        .populate('vehicleModel', 'modelName');

    if (!booking) throw { message: 'Booking not found', statusCode: 404 };

    let vehicle = null;
    let finalDriverId = driverId;

    // If vehicleId provided, validate and assign vehicle
    if (vehicleId) {
        vehicle = await Vehicle.findById(vehicleId)
            .populate('model', 'modelName')
            .populate('group', 'groupName')
            .populate('assignedDriver', 'fullName phone status');

        if (!vehicle) throw { message: 'Vehicle not found', statusCode: 404 };

        // Validate vehicle matches booking's group and model (if specified)
        if (booking.vehicleGroup && vehicle.group._id.toString() !== booking.vehicleGroup._id.toString()) {
            throw {
                message: `Vehicle group mismatch. Booking requires ${booking.vehicleGroup.groupName || 'different group'}`,
                statusCode: 400
            };
        }

        if (booking.vehicleModel && vehicle.model._id.toString() !== booking.vehicleModel._id.toString()) {
            throw {
                message: `Vehicle model mismatch. Booking requires ${booking.vehicleModel.modelName || 'different model'}`,
                statusCode: 400
            };
        }

        // Check if vehicle is available for booking
        if (vehicle.operationalStatus !== 'active' && vehicle.status !== 'active') {
            throw { message: 'Vehicle is not active/available', statusCode: 400 };
        }

        if (vehicle.bookingStatus === 'ontrip') {
            throw { message: 'Vehicle is currently on another trip', statusCode: 400 };
        }

        // Assign vehicle to booking
        booking.vehicle = vehicleId;

        // If vehicle has assigned driver and no driverId provided, use vehicle's driver
        if (!driverId && vehicle.assignedDriver) {
            finalDriverId = vehicle.assignedDriver._id;
        }
    }

    // If no driver ID at this point, error
    if (!finalDriverId) {
        throw { message: 'Driver ID is required (or select a vehicle with an assigned driver)', statusCode: 400 };
    }

    const driver = await Driver.findById(finalDriverId).select('fullName phone status');
    if (!driver) throw { message: 'Driver not found', statusCode: 404 };

    if (driver.status !== 'active' && driver.status !== 'assigned') {
        throw { message: 'Driver is not active', statusCode: 400 };
    }

    // Update driver status
    driver.status = 'assigned';
    await driver.save();

    // Check if booking already has this driver assigned
    if (booking.driver && booking.driver._id.toString() === finalDriverId.toString()) {
        // Same driver - just update vehicle if provided
        if (vehicleId && booking.vehicle?.toString() !== vehicleId) {
            await vehicleService.updateBookingStatus(vehicleId, 'booked');
            await booking.save();
            return { driver, vehicle, message: 'Vehicle updated for existing driver assignment' };
        }
        throw { message: 'Driver is already assigned to this booking', statusCode: 400 };
    }

    const previousDriver = booking.driver;
    booking.driver = finalDriverId;

    // Update vehicle booking status to 'booked' when assigned
    if (vehicleId) {
        await vehicleService.updateBookingStatus(vehicleId, 'booked');
    }

    // Only update status and add timeline entry if not already assigned
    if (booking.status !== 'assigned') {
        booking.status = 'assigned';

        // Add "Driver & Vehicle Assigned" timeline
        await timelineService.addTimelineEntry(booking._id, {
            title: 'Driver Assigned',
            status: 'assigned',
            description: `Driver ${driver.fullName} assigned to booking`
        });
    }

    await booking.save();

    // Auto-populate driver details from vehicle if not provided? 
    // Already handled by fetching driver from ID.

    // Trigger Webhook for Driver Assignment
    webhookService.triggerWebhook('driver_assigned', booking);

    // Return populated booking
    return booking;
};

/**
 * Unassign driver from booking
 * @param {String} bookingId 
 */
exports.unassignDriver = async (bookingId) => {
    const booking = await Booking.findById(bookingId);

    if (!booking) {
        throw { message: 'Booking not found', statusCode: 404 };
    }

    // Safety check: Cannot unassign if trip already started or completed
    if (['inProgress', 'completed', 'cancelled'].includes(booking.status)) {
        throw { message: `Cannot unassign driver when booking is ${booking.status}`, statusCode: 400 };
    }

    // 1. Revert Driver Status to 'active'
    if (booking.driver) {
        await Driver.findByIdAndUpdate(booking.driver, { status: 'active' });
    }

    // 2. Revert Vehicle Status to 'active' logic if needed
    // Assuming vehicles don't have a 'booked' status to revert, or it's implicitly handled.
    // If vehicle status tracks 'ontrip', ensure it's reset if it was set separately.
    if (booking.vehicle) {
        // Find vehicle and if it was marked as something else, revert. 
        // Based on assign logic, we only checked status. We didn't change vehicle status to 'booked' in assignDriver explicitly unless in that one conditional block. 
        // But let's be safe.
        // await Vehicle.findByIdAndUpdate(booking.vehicle, { status: 'active' }); 
        // Actually vehicle status usually stays Active unless maintenance/inactive. 
        // Operational status might be relevant. Let's leave vehicle status alone for now to avoid side effects, 
        // unless you have specific logic for vehicle state management during assignment.
    }

    // 3. Clear Assignment from Booking
    booking.driver = undefined;
    booking.vehicle = undefined;
    booking.status = 'created'; // Revert status to Created (Available for assignment)

    await booking.save();

    // 4. Add Timeline Entry
    await timelineService.addTimelineEntry(booking._id, {
        title: 'Driver Unassigned',
        status: 'created',
        description: 'Driver was unassigned by admin. Booking is now open for new assignment.'
    });

    return booking;
};



/**
 * Update booking status
 * @param {String} bookingId - Booking ID
 * @param {String} status - New status
 * @param {String} description - Optional description
 */
exports.updateBookingStatus = async (bookingId, status, description) => {
    const booking = await Booking.findById(bookingId);
    if (!booking) throw { message: 'Booking not found', statusCode: 404 };

    booking.status = status;

    let title = 'Status Updated';
    let finalDescription = description || `Status updated to ${status}`;

    if (status === 'enRoute') {
        title = 'Driver En Route';
        finalDescription = 'Driver is on the way to pickup location';
    } else if (status === 'arrived') {
        title = 'Driver Arrived';
        finalDescription = 'Driver has arrived at the pickup location';
    }

    await timelineService.addTimelineEntry(booking._id, {
        title,
        status,
        description: finalDescription
    });

    await booking.save();

    // Trigger Webhook for Status Update
    if (status === 'arrived') {
        webhookService.triggerWebhook('driver_arrived', booking);
    } else if (status === 'cancelled') {
        webhookService.triggerWebhook('booking_cancelled', booking);
    }

    return booking;
};

/**
 * Start the trip with OTP verification
 */
exports.startTrip = async (bookingId, otp, driverId) => {
    const booking = await Booking.findById(bookingId).populate('customer driver vehicle');
    if (!booking) throw { message: 'Booking not found', statusCode: 404 };

    // Verify driver
    if (booking.driver._id.toString() !== driverId.toString()) {
        throw { message: 'Unauthorized: Only the assigned driver can start the trip', statusCode: 403 };
    }

    // Verify OTP
    await verifyOtp(booking.customer.phone, otp, 'phone');

    // Update statuses
    booking.status = 'inProgress';

    // Update Driver status
    await Driver.findByIdAndUpdate(booking.driver._id, { status: 'ontrip' });

    // Update Vehicle booking status to "ontrip" using new status system
    await vehicleService.updateBookingStatus(booking.vehicle._id, 'ontrip');

    await timelineService.addTimelineEntry(booking._id, {
        title: 'Customer Picked Up',
        status: 'inProgress',
        description: 'Trip started towards destination'
    });

    await booking.save();

    // Trigger Webhook for Trip Started
    webhookService.triggerWebhook('trip_started', booking);

    return booking;
};

/**
 * End the trip
 */
exports.endTrip = async (bookingId, driverId, paymentDetails = {}) => {
    const booking = await Booking.findById(bookingId).populate('customer driver vehicle vendor');
    if (!booking) throw { message: 'Booking not found', statusCode: 404 };

    // Verify driver
    if (booking.driver._id.toString() !== driverId.toString()) {
        throw { message: 'Unauthorized: Only the assigned driver can end the trip', statusCode: 403 };
    }

    // Update payment details if provided (for split payments)
    if (paymentDetails.onlinePaid !== undefined) {
        booking.payment.onlinePaid = Math.round(paymentDetails.onlinePaid || 0);
    }
    if (paymentDetails.offlinePaid !== undefined) {
        booking.payment.offlinePaid = Math.round(paymentDetails.offlinePaid || 0);
    }

    // If payment mode is cash, set offlinePaid to total
    if (booking.payment.mode === 'cash') {
        booking.payment.offlinePaid = booking.fare.total;
        booking.payment.onlinePaid = 0;
    } else if (booking.payment.mode === 'online') {
        booking.payment.onlinePaid = booking.fare.total;
        booking.payment.offlinePaid = 0;
    }

    // Update statuses
    booking.status = 'completed';
    booking.payment.status = 'paid';

    // Update Driver status - set back to active
    await Driver.findByIdAndUpdate(booking.driver._id, { status: 'active' });

    // Update Vehicle booking status to "available" using new status system
    await vehicleService.updateBookingStatus(booking.vehicle._id, 'available');

    // Check if vendor was deactivated during trip - if so, deactivate driver and vehicle now
    const vehicle = await Vehicle.findById(booking.vehicle._id).populate('vendor');
    if (vehicle && vehicle.vendor) {
        const vendorStatus = vehicle.vendor.status;
        const vendorDeleted = vehicle.vendor.isDeleted;

        if (vendorDeleted || ['inactive', 'suspended', 'rejected'].includes(vendorStatus)) {
            // Deactivate driver
            await Driver.findByIdAndUpdate(booking.driver._id, {
                previousStatus: 'active',
                status: 'inactive'
            });

            // Deactivate vehicle
            vehicle.previousOperationalStatus = vehicle.operationalStatus;
            vehicle.operationalStatus = 'inactive';
            vehicle.status = 'inactive';
            await vehicle.save();
        }
    }

    await timelineService.addTimelineEntry(booking._id, {
        title: 'Trip Completed',
        status: 'completed',
        description: 'Customer reached destination safely. Trip completed successfully.'
    });

    await booking.save();

    // Trigger Webhook for Trip Completed
    webhookService.triggerWebhook('trip_completed', booking);

    // Process settlement after trip completion
    try {
        const settlementResult = await settlementService.processBookingSettlement(booking._id);
        booking._doc.settlement = settlementResult.settlement;

        // Send trip completed email with invoice
        setImmediate(async () => {
            try {
                // Import locally to avoid circular dependency loop if any
                const emailNotificationService = require('./email-notification.service');
                await emailNotificationService.sendTripCompletedEmail(booking);
            } catch (emailError) {
                console.error('Failed to send trip completed email:', emailError.message);
            }
        });

    } catch (settlementError) {
        console.error('Settlement processing failed:', settlementError);
        // Don't fail the trip completion, just log the error
        // Settlement can be processed manually later
    }

    return booking;
};

/**
 * Get live orders with counts
 * @param {Number} limit - Max orders to return
 * @param {Number} page - Page number
 */
exports.getLiveOrdersWithCounts = async (limit = 20, page = 1, vendorId, zoneId) => {
    const activeStatuses = ['created', 'assigned', 'arrived', 'inProgress', 'enRoute'];

    const query = { status: { $in: activeStatuses } };
    if (vendorId) {
        query.vendor = new mongoose.Types.ObjectId(vendorId);
    }
    if (zoneId) {
        query.zone = new mongoose.Types.ObjectId(zoneId);
    }

    const skip = (parseInt(page) - 1) * parseInt(limit);

    const [liveOrders, totalActive, statusCounts] = await Promise.all([
        Booking.find(query)
            .populate('customer', 'fullName phone')
            .populate('driver', 'fullName phone')
            .populate('zone', 'zoneName')
            .select('orderId bookingId status pickupLocation dropLocation fare.total totalFare tripType createdAt scheduledAt vendor')
            .sort({ createdAt: -1 })
            .skip(skip)
            .limit(parseInt(limit)),
        Booking.countDocuments(query),
        Booking.aggregate([
            { $match: query },
            { $group: { _id: '$status', count: { $sum: 1 } } }
        ])
    ]);

    const counts = { Created: 0, Assigned: 0, Arrived: 0, InProgress: 0, total: totalActive };
    statusCounts.forEach(s => {
        // Map db status to PascalCase key: inProgress -> InProgress, enRoute -> InProgress, etc.
        const key = s._id.charAt(0).toUpperCase() + s._id.slice(1);
        if (counts.hasOwnProperty(key)) {
            counts[key] = s.count;
        } else if (s._id === 'enRoute') {
            counts.InProgress += s.count;
        }
    });

    const formattedOrders = liveOrders.map(order => {
        const orderObj = order.toObject();
        if (orderObj.status) {
            // inProgress -> InProgress
            orderObj.status = orderObj.status.charAt(0).toUpperCase() + orderObj.status.slice(1);
        }
        // Add totalFare for frontend compatibility
        orderObj.totalFare = orderObj.fare?.total || 0;
        return orderObj;
    });

    const totalPages = Math.ceil(totalActive / parseInt(limit));

    return {
        liveOrders: formattedOrders,
        counts,
        pagination: {
            total: totalActive,
            totalPages,
            currentPage: parseInt(page),
            limit: parseInt(limit)
        }
    };
};

exports.searchAvailableCabs = async (searchParams, pagination) => {
    const {
        sourceLat, sourceLong, destLat, destLong,
        tripType, parsedStops, vehicleType, zoneId, vendorId,
        fuelType, isAc, startTime,
        rentalPackage, // { hours, distance } for rental trips
        distance: preCalculatedDistance // Pre-calculated distance from SEO routes (Google Maps API)
    } = searchParams;
    const { page = 1, limit = 10 } = pagination;

    // ==========================================
    // STEP 1: Calculate Distance Based on Trip Type
    // ==========================================
    let totalDistance = 0;
    let displayDistance = 0;
    let displayDistanceLabel = '';
    let pricingSource = 'route';
    let estimatedTotalDuration = 0;

    if (tripType === 'rental') {
        // Rental: Use package distance, NOT actual route
        if (!rentalPackage || !rentalPackage.hours || !rentalPackage.distance) {
            throw { message: 'Rental package (hours, distance) is required for rental trips', statusCode: 400 };
        }
        totalDistance = rentalPackage.distance;
        displayDistance = rentalPackage.distance;
        displayDistanceLabel = `${rentalPackage.hours}h / ${rentalPackage.distance}km Package`;
        pricingSource = 'package';
    } else if (preCalculatedDistance && preCalculatedDistance > 0) {
        // Use pre-calculated distance from SEO routes (Google Maps API - accurate road distance)
        totalDistance = preCalculatedDistance;
        displayDistance = Math.round(preCalculatedDistance * 10) / 10;
        pricingSource = 'google_maps';
        console.log(`Using pre-calculated Google Maps distance: ${displayDistance} km`);
    } else {
        // Calculate using Google Maps API for accurate road distance
        let prevLat = parseFloat(sourceLat);
        let prevLong = parseFloat(sourceLong);

        // Calculate distance through stops using Google Maps API
        for (const stop of parsedStops) {
            const coords = stop.coordinates || (stop.value?.coordinates) || (stop.value?.latitude && stop.value?.longitude ? [stop.value.longitude, stop.value.latitude] : null);
            if (coords && coords.length === 2) {
                const result = await exports.calculateDistanceAsync(prevLat, prevLong, coords[1], coords[0]);
                totalDistance += result.distance;
                estimatedTotalDuration += result.duration;
                prevLat = coords[1];
                prevLong = coords[0];
            }
        }

        // Calculate final leg to destination using Google Maps API
        if (destLat && destLong) {
            const result = await exports.calculateDistanceAsync(prevLat, prevLong, parseFloat(destLat), parseFloat(destLong));
            totalDistance += result.distance;
            estimatedTotalDuration += result.duration;
            pricingSource = 'google_maps';
            console.log(`Google Maps calculated total distance: ${totalDistance} km, duration: ${estimatedTotalDuration} mins`);
        }

        displayDistance = Math.round(totalDistance * 10) / 10;

        if (tripType === 'roundTrip') {
            displayDistanceLabel = `${displayDistance} km (one way)`;
        } else if (tripType === 'airport') {
            displayDistanceLabel = 'Airport Transfer';
            pricingSource = 'fixed';
        } else {
            displayDistanceLabel = `${displayDistance} km`;
        }
    }

    // Use Google Maps duration if calculated, otherwise use package hours for rental
    let estimatedDuration;
    if (tripType === 'rental') {
        estimatedDuration = rentalPackage.hours * 60;
    } else if (pricingSource === 'google_maps' || pricingSource === 'package') {
        // Use the duration from above calculations (estimatedTotalDuration)
        estimatedDuration = estimatedTotalDuration || Math.round((totalDistance / 40) * 60);
    } else {
        estimatedDuration = Math.round((totalDistance / 40) * 60);
    }

    // ==========================================
    // STEP 2: Find Matching Zone
    // ==========================================
    const zones = await Zone.find({ isActive: true });

    if (zones.length === 0) {
        throw { message: 'No service available in this area', statusCode: 404 };
    }

    // ==========================================
    // STEP 2.5: Get Blocked Vendors (Settlement Check)
    // ==========================================
    const blockedVendors = await Vendor.find({
        isDeleted: false,
        $expr: { $lt: ['$wallet.balance', '$maxDueLimit'] }
    }).select('_id');
    const blockedVendorIds = blockedVendors.map(v => v._id);


    let targetZoneId = zoneId;

    // Find matching zone based on source coordinates
    if (!targetZoneId) {
        // Try to find zone that contains the pickup location
        const matchingZone = zones.find(z =>
            z.coordinates && z.coordinates.length > 0 &&
            exports.isPointInPolygon({ lat: parseFloat(sourceLat), lng: parseFloat(sourceLong) }, z.coordinates)
        );

        if (matchingZone) {
            targetZoneId = matchingZone._id;
        } else {
            console.log('No matching zone found for coordinates');
            throw { message: 'No service available in this area', statusCode: 404 };
        }
    }

    // ==========================================
    // STEP 3: Query Active Vehicles
    // ==========================================
    // Simple query - just match zone and active status
    let vehicleQuery = {
        zone: targetZoneId
    };

    // Add status filter - check both old and new status fields
    vehicleQuery.$or = [
        { status: 'active' },
        { operationalStatus: 'active' }
    ];

    if (vendorId) vehicleQuery.vendor = vendorId;

    // Exclude blocked vendors
    if (blockedVendorIds.length > 0) {
        if (vehicleQuery.vendor) {
            // If specific vendor requested, check if blocked
            if (blockedVendorIds.some(id => id.toString() === vehicleQuery.vendor.toString())) {
                return { vehicles: [], totalCount: 0, totalDistance: displayDistance, displayDistanceLabel, estimatedDuration, pricingSource };
            }
        } else {
            vehicleQuery.vendor = { $nin: blockedVendorIds };
        }
    }

    if (vehicleType) {
        vehicleQuery.vehicleName = { $regex: vehicleType, $options: 'i' };
    }

    console.log('=== SEARCH DEBUG ===');
    console.log('Target Zone ID:', targetZoneId);
    console.log('Vehicle Query:', JSON.stringify(vehicleQuery, null, 2));

    // First, let's check how many vehicles exist in this zone (without status filter)
    const allVehiclesInZone = await Vehicle.countDocuments({ zone: targetZoneId });
    console.log('Total vehicles in zone (any status):', allVehiclesInZone);

    // Check with just status filter
    const activeVehicles = await Vehicle.countDocuments(vehicleQuery);
    console.log('Vehicles matching query:', activeVehicles);
    const vehicles = await Vehicle.find(vehicleQuery).select("-vehicleImages -requiredDocuments")
        .populate('zone')
        .populate({ path: 'group', select: '-vehicleImages' })
        .populate({
            path: 'model',
            populate: [
                { path: 'brandName', select: 'brandName' },
                { path: 'category', select: 'categoryName defaultSeats' }
            ]
        })
        .skip((parseInt(page) - 1) * parseInt(limit))
        .limit(parseInt(limit));

    const totalCount = await Vehicle.countDocuments(vehicleQuery);

    // ==========================================
    // STEP 4: Calculate Fare for Each Vehicle (Trip-Type Specific)
    // ==========================================
    const totalWaitTime = parsedStops.reduce((sum, stop) => sum + (stop.waitTime || stop.value?.waitTime || 0), 0);
    const scheduledTime = startTime ? new Date(startTime) : new Date();

    const enrichedVehicles = vehicles.map(vehicle => {
        const group = vehicle.group;
        const zone = vehicle.zone;
        const vehicleFuelType = fuelType || 'petrol';
        const vehicleIsAc = isAc !== undefined ? isAc : (vehicle.model?.acAvailable ?? true);

        let fare;
        let rentalDetails = null;

        if (tripType === 'rental') {
            // RENTAL: Package-based pricing
            const pricing = group?.fuelPricing?.find(p => p.fuelType === vehicleFuelType) || group?.fuelPricing?.[0];
            const baseFare = vehicleIsAc ? (pricing?.acBaseFare || 0) : (pricing?.nonAcBaseFare || 0);
            const ratePerKm = vehicleIsAc ? (pricing?.ratePerKmAc || 0) : (pricing?.ratePerKmNonAc || 0);

            // Package multipliers based on hours
            const packageMultipliers = {
                2: 1.0, 4: 1.8, 6: 2.5, 8: 3.2, 10: 4.0, 12: 4.8
            };
            const multiplier = packageMultipliers[rentalPackage.hours] || (rentalPackage.hours * 0.4);

            const packageBase = Math.round((baseFare + (rentalPackage.distance * ratePerKm)) * multiplier);
            const extraKmRate = Math.round(ratePerKm * 1.2);
            const extraHourRate = Math.round(baseFare * 0.15);

            // Apply night charge to package price
            let nightCharge = 0;
            if (isNightTime(scheduledTime, group?.nightStartTime, group?.nightEndTime)) {
                if (group?.nightChargeType === 'percentage') {
                    nightCharge = Math.round((packageBase * (group.nightChargeValue || 0)) / 100);
                } else {
                    nightCharge = group?.nightChargeValue || 0;
                }
            }

            // Zone extra charge
            let zoneExtraCharge = 0;
            if (zone?.extraFareRule?.fareAmount) {
                if (zone.extraFareRule.fareType === 'percentage') {
                    zoneExtraCharge = Math.round((packageBase * zone.extraFareRule.fareAmount) / 100);
                } else {
                    zoneExtraCharge = zone.extraFareRule.fareAmount;
                }
            }

            fare = {
                base: packageBase,
                distance: 0,
                stops: 0,
                nightCharge,
                zoneExtraCharge: Math.round(zoneExtraCharge),
                taxes: 0,
                discount: 0,
                total: Math.round(packageBase + nightCharge + zoneExtraCharge)
            };

            rentalDetails = {
                packageName: displayDistanceLabel,
                includedKm: rentalPackage.distance,
                includedHours: rentalPackage.hours,
                extraKmCharge: extraKmRate,
                extraHourCharge: extraHourRate
            };
        } else {
            // oneWay/roundTrip/airport: Use existing calculateFare
            fare = exports.calculateFare(tripType, {
                distance: totalDistance,
                stopsCount: parsedStops.length,
                totalWaitTime,
                rentalPackage: null,
                group,
                zone,
                fuelType: vehicleFuelType,
                isAc: vehicleIsAc,
                scheduledTime
            });
        }

        const brandName = vehicle.model?.brandName?.brandName || "";
        const modelName = vehicle.model?.modelName || "Standard";
        const displayName = brandName ? `${brandName} ${modelName}` : modelName;

        const vehicleData = vehicle.toObject();
        delete vehicleData.zone;
        delete vehicleData.vendor;

        return {
            ...vehicleData,
            modelName: displayName,
            fare,
            rentalDetails,
            tripDetails: {
                totalDistance: displayDistance,
                displayDistanceLabel,
                estimatedDuration,
                stopsCount: parsedStops.length,
                pricingSource
            }
        };
    });

    return {
        vehicles: enrichedVehicles,
        totalCount,
        totalDistance: displayDistance,
        displayDistanceLabel,
        estimatedDuration,
        pricingSource
    };
};

exports.getVehicleById = async (id) => {
    const vehicle = await Vehicle.findById(id);
    return vehicle;
};


/**
 * Check if a point is inside a polygon (ray-casting algorithm)
 * @param {Object} point - {lat, lng}
 * @param {Array} polygon - Array of {lat, lng}
 */
exports.searchCab = async (query) => {
    const {
        pickup, drop,
        tripType = 'oneWay',
        fuelType = 'petrol',
        isAc = true,
        scheduledTime,
        distance: queryDistance // Distance from query (in km)
    } = query;

    const limit = Number(query.limit) || 20;
    const page = Number(query.page) || 1;

    // 1. Fetch active zones
    const zones = await Zone.find({ isActive: true }).lean();
    if (!zones.length) {
        return { vehicles: [], total: 0, pages: 0, page, zone: null };
    }

    // 2. Find the matching zone based on pickup coordinates
    let matchedZone = null;

    // First, try to find a zone that contains the point AND has vehicles
    for (const zone of zones) {
        if (isPointInPolygon(pickup, zone.coordinates)) {
            // Check if this zone has vehicles
            const vehicleCount = await Vehicle.countDocuments({ zone: zone._id, status: 'active' });
            if (vehicleCount > 0) {
                matchedZone = zone;
                break;
            }
        }
    }

    // 3. Fallback Logic (if no matching zone with vehicles found)
    // 3. Fallback Logic (if no matching zone with vehicles found)
    if (!matchedZone) {
        console.log('SearchCab: No matching zone with vehicles found for coordinates', pickup);
        // We return empty if no zone covers the pickup location
        // This ensures "proper searching functionality" where only relevant zones are returned.
        return { vehicles: [], total: 0, pages: 0, page, zone: null };
    }

    // Safety check if still no zone (should be covered by line 1088 but good to be safe)
    if (!matchedZone) {
        return { vehicles: [], total: 0, pages: 0, page, zone: null };
    }

    // 4. Use distance and duration from query if provided, otherwise calculate using Google Maps API
    let distance = 0;
    let tripDuration = 0;

    if (queryDistance && !isNaN(queryDistance)) {
        distance = parseFloat(queryDistance);
        // Estimate duration based on average speed of 40 km/h if not provided from Google
        tripDuration = Math.round((distance / 40) * 60);
    } else if (drop && !isNaN(drop.lat) && !isNaN(drop.lng)) {
        const result = await exports.calculateDistanceAsync(
            parseFloat(pickup.lat), parseFloat(pickup.lng),
            parseFloat(drop.lat), parseFloat(drop.lng)
        );
        distance = result.distance;
        tripDuration = result.duration;
        console.log(`SearchCab: Google Maps distance: ${distance} km, duration: ${tripDuration} mins`);
    }

    // 4.5. Get blocked vendors (due limit exceeded) - SETTLEMENT SYSTEM INTEGRATION
    const blockedVendors = await Vendor.find({
        isDeleted: false,
        $expr: { $lt: ['$wallet.balance', '$maxDueLimit'] }
    }).select('_id');
    const blockedVendorIds = blockedVendors.map(v => v._id);

    // 5. Fetch vehicles using vehicle service (with full population including group)
    // Exclude vehicles from blocked vendors
    const vehicleResult = await vehicleService.getVehiclesWithFilters(
        {
            zoneId: matchedZone._id,
            status: 'active',
            excludeVendors: blockedVendorIds // Pass blocked vendor IDs to exclude
        },
        { page, limit }
    );

    // 6. Enrich each vehicle with fare calculation and detailed info
    const enrichedVehicles = vehicleResult.list.map(vehicle => {
        const group = vehicle.group || {};
        const category = vehicle.category || {};
        const brand = vehicle.brand || {};

        // Get pricing for the requested fuel type (or first available)
        const pricing = group.fuelPricing?.find(p => p.fuelType === fuelType) || group.fuelPricing?.[0] || {};

        // Calculate fare using existing calculateFare function (same as ride creation)
        const fare = exports.calculateFare(tripType, {
            distance,
            stopsCount: 0,
            totalWaitTime: 0,
            rentalPackage: null,
            group,
            zone: matchedZone,
            fuelType: pricing.fuelType || fuelType,
            isAc,
            scheduledTime: scheduledTime ? new Date(scheduledTime) : new Date()
        });

        // Calculate included KMs and extra rate
        const includedKm = pricing.baseKm || 0;
        const extraKmRate = isAc ? (pricing.ratePerKmAc || 0) : (pricing.ratePerKmNonAc || 0);

        // Calculate discount from group discount settings
        const discountResult = vehicleService.calculateDiscountAmount(fare.total, group.discount);
        const discountAmount = discountResult.discountAmount;
        const originalPrice = fare.total;
        const finalPrice = Math.max(0, fare.total - discountAmount);

        return {
            vehicleId: vehicle._id,
            vehicleName: vehicle.vehicleName || `${brand.brandName || ''}, ${vehicle.model?.modelName || ''}`.trim(),
            orSimilar: true, // Flag to show "Or Similar" badge
            registrationNumber: vehicle.registrationNumber,

            // Vehicle Details (like Goibibo image)
            category: category.categoryName || 'Standard',
            isAc: isAc,
            seats: group.seats || category.defaultSeats || 4,
            tier: group.tier || 'Economy',
            description: group.description || `${group.tier || 'Economy'} Car With ${pricing.luggageCapacity ? 'Limited' : 'Standard'} Luggage`,

            // Vehicle Images FROM GROUP (for display purposes, not vendor-uploaded)
            images: group.vehicleImages || {},

            // Fuel & Features
            fuelType: pricing.fuelType || fuelType,
            bootSpace: pricing.bootSpace || 'available',
            roofCarrier: pricing.roof || 'unavailable',
            luggageCapacity: pricing.luggageCapacity || 0,

            // Pricing Details (same calculation as ride creation)
            fare: {
                ...fare,
                currency: 'INR'
            },
            includedKm,
            extraKmRate,

            // Discount Info (from group discount settings)
            discount: {
                hasDiscount: discountResult.isValid,
                discountType: discountResult.discountType || null,
                discountValue: discountResult.discountValue || 0,
                discountAmount: discountAmount,
                description: discountResult.description || null
            },
            originalPrice,
            finalPrice,
            taxesAndFees: fare.taxes || 0,

            // Cancellation Policy
            cancellationPolicy: group.cancellationPolicy || 'Free Cancellation Until 1 Hour Before Pickup',

            // Reserve Amount (20% of final price)
            reserveAmount: Math.round(finalPrice * 0.2),

            // Zone Info
            zone: {
                id: matchedZone._id,
                name: matchedZone.zoneName
            },

            // Vendor Info
            vendor: vehicle.vendor ? {
                id: vehicle.vendor._id,
                name: vehicle.vendor.vendorName
            } : null,

            // Trip Details
            tripDetails: {
                distance: Math.round(distance * 10) / 10,
                estimatedDuration: tripDuration || Math.round((distance / 40) * 60)
            }
        };
    });

    return {
        vehicles: enrichedVehicles,
        total: vehicleResult.total,
        pages: vehicleResult.pages,
        page: vehicleResult.page,
        zone: matchedZone.zoneName,
        distance: Math.round(distance * 10) / 10
    };
};

// isPointInPolygon moved to top

exports.getBookingReview = async (params) => {
    const { vehicleId, pickup, drop, distance, tripType, fuelType, isAc, scheduledTime, promoCode, rentalPackage } = params;

    // 1. Fetch the vehicle with full population (same as createBooking)
    let vehicle = await Vehicle.findById(vehicleId)
        .populate('vendor', 'vendorName vendorContact')
        .populate('zone') // Full zone for extraFareRule
        .populate('group') // Direct field - contains fuelPricing, vehicleImages, etc.
        .populate('category', 'categoryName defaultSeats')
        .populate('brand', 'brandName logo')
        .populate('model', 'modelName');

    // 1.5 Fallback: If vehicle not found, check if ID is a VehicleGroup and find an available vehicle
    if (!vehicle) {
        // Check if it's a valid ObjectId first to avoid cast errors
        if (mongoose.Types.ObjectId.isValid(vehicleId)) {
            const group = await VehicleGroup.findById(vehicleId);
            console.log('[getBookingReview] Group lookup for ID:', vehicleId, '-> Found:', !!group, group?.groupName);

            if (group) {
                // Find matching zone for pickup
                const zones = await Zone.find({ isActive: true }).lean();
                console.log('[getBookingReview] Pickup coordinates:', pickup);
                console.log('[getBookingReview] Active zones count:', zones.length);

                const matchedZone = zones.find(z => {
                    if (!z.coordinates || z.coordinates.length === 0) {
                        console.log('[getBookingReview] Zone', z.zoneName, 'has no coordinates');
                        return false;
                    }
                    const isInside = isPointInPolygon({ lat: pickup.lat, lng: pickup.lng }, z.coordinates);
                    console.log('[getBookingReview] Zone', z.zoneName, '-> Point in polygon:', isInside);
                    return isInside;
                });

                console.log('[getBookingReview] Matched zone:', matchedZone?.zoneName, matchedZone?._id);

                if (matchedZone) {
                    // Find an available vehicle in this group and zone
                    // Check operationalStatus='active' AND bookingStatus='available'
                    const vehicleQuery = {
                        group: group._id,
                        zone: matchedZone._id,
                        operationalStatus: 'active',
                        bookingStatus: 'available'
                    };
                    console.log('[getBookingReview] Vehicle query:', JSON.stringify(vehicleQuery));

                    // Count how many match
                    const vehicleCount = await Vehicle.countDocuments(vehicleQuery);
                    console.log('[getBookingReview] Matching vehicles count:', vehicleCount);

                    vehicle = await Vehicle.findOne(vehicleQuery)
                        .populate('vendor', 'vendorName vendorContact')
                        .populate('zone')
                        .populate('group')
                        .populate('category', 'categoryName defaultSeats')
                        .populate('brand', 'brandName logo')
                        .populate('model', 'modelName');

                    console.log('[getBookingReview] Found vehicle:', vehicle?.registrationNumber);
                } else {
                    console.log('[getBookingReview] No zone matched the pickup coordinates!');
                }
            }
        }
    }

    if (!vehicle) {
        throw {
            message: 'No vehicles available for this selection. All vehicles in this category may be currently booked or inactive. Please try a different category or check back later.',
            statusCode: 404
        };
    }

    const group = vehicle.group || {};
    const category = vehicle.category || {};
    const brand = vehicle.brand || {};
    const zone = vehicle.zone || {};

    // 2a. Build displayName from model names (without brand prefix) - same as search page
    // Get unique model IDs from active vehicles in this group and zone
    const activeVehicles = await Vehicle.find({
        group: group._id,
        zone: zone._id,
        $or: [{ bookingStatus: 'available' }, { operationalStatus: 'active' }]
    }).select('model').lean();

    const modelIds = [...new Set(activeVehicles.map(v => v.model).filter(m => m))];
    const models = await VehicleModel.find({ _id: { $in: modelIds } })
        .select('modelName vehicleImages')
        .lean();

    // Build model names array (without brand)
    const modelNames = models.map(m => m.modelName || '').filter(n => n);
    const displayName = modelNames.length > 0 ? modelNames.join(', ') : (group.groupName || 'Standard');

    // Get best image: from model vehicleImages or group vehicleImages
    const primaryModel = models[0] || {};
    const modelImages = primaryModel.vehicleImages || [];
    const vehicleImage = modelImages.find(img => img.view === 'front')?.url
        || modelImages[0]?.url
        || group.vehicleImages?.frontView
        || null;

    // 2. Get pricing for the requested fuel type
    const pricing = group.fuelPricing?.find(p => p.fuelType === fuelType) || group.fuelPricing?.[0] || {};

    // For rental trips, check if this group has matching rentalPricing
    let rentalOption = null;
    let actualDistance = distance;
    if (tripType === 'rental' && rentalPackage) {
        actualDistance = rentalPackage.distance || distance;
        rentalOption = group.rentalPricing?.find(rp =>
            rp.hours === rentalPackage.hours &&
            (rp.fuelType === fuelType || !rp.fuelType || rp.fuelType === 'petrol')
        );
        // Also try matching just by hours if fuelType didn't match
        if (!rentalOption) {
            rentalOption = group.rentalPricing?.find(rp => rp.hours === rentalPackage.hours);
        }
    }

    // 3. Calculate fare using existing calculateFare function (same as ride creation)
    const fare = exports.calculateFare(tripType, {
        distance: actualDistance,
        stopsCount: 0,
        totalWaitTime: 0,
        rentalPackage: rentalPackage || null,
        group,
        zone, // Ensure zone is passed
        fuelType: pricing.fuelType || fuelType,
        isAc,
        scheduledTime: scheduledTime ? new Date(scheduledTime) : new Date()
    });

    // 4. Calculate included KMs and extra rate
    const includedKm = rentalOption ? rentalOption.baseKm : (pricing.baseKm || 0);
    const extraKmRate = rentalOption ? rentalOption.extraFarePerKm : (isAc ? (pricing.ratePerKmAc || 0) : (pricing.ratePerKmNonAc || 0));

    // 5. Calculate discount from group discount settings
    const groupDiscountResult = vehicleService.calculateDiscountAmount(fare.total, group.discount);
    let discountAmount = groupDiscountResult.discountAmount;

    // 6. Apply promo code on top of group discount (placeholder for future promo logic)
    let promoApplied = null;
    if (promoCode) {
        // TODO: Implement promo code validation logic
        // promoApplied = await validatePromoCode(promoCode, fare.total);
        // discountAmount += promoApplied?.discount || 0;
    }

    const originalTotal = fare.total;
    const finalTotal = Math.max(0, fare.total - discountAmount);

    // 7. Calculate payment options
    const reserveAmount = Math.round(finalTotal * 0.2); // 20% to reserve
    const remainingAmount = finalTotal - reserveAmount;

    // 7. Format scheduled time for display
    const scheduledDate = new Date(scheduledTime);
    const formattedDate = scheduledDate.toLocaleDateString('en-IN', {
        day: '2-digit', month: 'short', year: '2-digit'
    });
    const formattedTime = scheduledDate.toLocaleTimeString('en-IN', {
        hour: '2-digit', minute: '2-digit', hour12: true
    });

    // 8. Build comprehensive review response (matching Goibibo UI)
    return {
        // Route Info
        route: {
            source: pickup.city || 'Pickup Location',
            destination: drop.city || 'Drop Location',
            tripType: tripType,
            tripTypeLabel: tripType === 'oneWay' ? 'Outstation-Oneway' :
                tripType === 'roundTrip' ? 'Outstation-RoundTrip' :
                    tripType === 'rental' ? 'Rental' : 'Airport',
            pickupDate: formattedDate,
            pickupTime: formattedTime,
            distance: tripType === 'rental' && rentalPackage ? rentalPackage.distance : Math.round(distance * 10) / 10
        },

        // Rental-specific details
        rentalDetails: (tripType === 'rental' && rentalPackage) ? {
            packageHours: rentalPackage.hours,
            packageKm: rentalPackage.distance,
            includedKm: rentalOption?.baseKm || rentalPackage.distance,
            baseFare: rentalOption?.baseFare || fare.base,
            extraFarePerKm: rentalOption?.extraFarePerKm || extraKmRate,
            hasRentalPricing: !!rentalOption
        } : null,

        // Vehicle Card Info
        vehicle: {
            id: vehicle._id,
            groupId: group._id, // Required for booking
            modelId: vehicle.model?._id, // Required for booking
            brandId: brand._id,
            registrationNumber: vehicle.registrationNumber,     // Added for translation
            categoryId: category._id, // Added for translation
            name: displayName, // Uses model names only (same as search page)
            image: vehicleImage, // Image from model or group
            orSimilar: true,
            category: category.categoryName || 'Standard',
            isAc: isAc,
            seats: group.seats || category.defaultSeats || 4,
            fuelType: pricing.fuelType || fuelType,
            includedKm,
            extraKmRate,
            cancellationPolicy: group.cancellationPolicyText || (Array.isArray(group.cancellationPolicy) && group.cancellationPolicy.length > 0 ? group.cancellationPolicy[0].description : 'Free Cancellation Until 1 Hour Before Pickup'),
            tier: group.tier || 'Economy',
            description: group.description || `${group.tier || 'Economy'} Car With Limited Luggage`
        },

        // Cab & Driver Details (will be assigned after booking)
        cabDriverDetails: {
            message: 'Cab Operator Will Be Assigned As Soon As Booking Is Complete.',
            shareDetails: `Your Cab And Driver Details Will Be Shared Via WhatsApp & SMS On Your Registered Mobile Number By ${formattedDate} At ${formattedTime}`
        },

        totalWithDiscount: finalTotal,
        reserveAmount,
        remainingAmount,
        // ADDED: Detailed Fare Breakdown for Logic Transparency
        breakdown: {
            baseFare: fare.baseFare,
            distanceFare: fare.distanceFare,
            nightCharges: fare.nightCharges,
            driverAllowance: fare.driverAllowance,
            airportCharges: fare.airportCharge || 0, // Explicitly expose this
            gst: fare.gst,
            totalBeforeDiscount: fare.total,
            discount: discountAmount
        },

        // Images of Selected Vehicle Category (FROM GROUP - not vendor uploaded)
        categoryImages: {
            note: 'Car Images Shown Here Are For Representation Purpose Only. Actual Car Will Belong The Same Model But May Not Be The Exact One As Shown In The Images',
            images: {
                all: [
                    group.vehicleImages?.frontView,
                    group.vehicleImages?.backView,
                    group.vehicleImages?.insideView,
                    group.vehicleImages?.bootView
                ].filter(Boolean),
                front: group.vehicleImages?.frontView || null,
                exterior: group.vehicleImages?.backView || null,
                interior: group.vehicleImages?.insideView || null,
                boot: group.vehicleImages?.bootView || null
            }
        },

        // Inclusions & Exclusions
        inclusionsExclusions: {
            included: [
                `${includedKm} Kms`,
                'State Taxes',
                'Parking Charges',
                'Only One Pickup And Drop'
            ],
            excluded: [
                'Toll Charges',
                `Fare Beyond ${includedKm} Kms: Rs ${extraKmRate}/Km`
            ]
        },

        // Pickup Address
        pickupAddress: {
            city: pickup.city || 'Enter City',
            state: pickup.state || '',
            address: pickup.address || 'Enter Pickup Location',
            coordinates: { lat: pickup.lat, lng: pickup.lng },
            note: 'Exact Location Needs To Be Updated'
        },

        // Drop Address
        dropAddress: drop.lat && drop.lng ? {
            city: drop.city || 'Destination',
            state: drop.state || '',
            address: drop.address || '',
            coordinates: { lat: drop.lat, lng: drop.lng }
        } : null,

        // Traveller Details (to be filled by user)
        travellerDetails: {
            required: true,
            fields: ['fullName', 'phone', 'email']
        },

        // Cancellation Policy (detailed)
        cancellationPolicy: {
            summary: group.cancellationPolicyText || (Array.isArray(group.cancellationPolicy) && group.cancellationPolicy.length > 0 ? group.cancellationPolicy[0].description : 'Free Cancellation Until 1 Hour Before Pickup'),
            details: [
                'Free cancellation till 1 hour before pickup',
                'Cancellation after that will incur charges',
                'No-show will be charged in full'
            ]
        },

        // Total Amount Section
        pricing: {
            fare: {
                ...fare,
                currency: 'INR'
            },
            includedKm,
            extraKmRate,
            discount: {
                hasDiscount: groupDiscountResult.isValid,
                discountType: groupDiscountResult.discountType || null,
                discountValue: groupDiscountResult.discountValue || 0,
                discountAmount: discountAmount,
                description: groupDiscountResult.description || null
            },
            promoApplied: promoApplied ? { code: promoCode, discount: discountAmount } : null,
            taxesAndFees: fare.taxes || 0,
            originalTotal,
            finalTotal
        },

        // Payment Options
        paymentOptions: [
            {
                type: 'partial',
                label: `Pay ₹${reserveAmount} Now`,
                description: 'Remaining To Driver',
                amount: reserveAmount,
                remainingAmount
            },
            {
                type: 'full',
                label: `Pay ₹${finalTotal} Now`,
                description: 'Full Payment',
                amount: finalTotal,
                remainingAmount: 0
            }
        ],

        // Offers Section
        offers: [
            {
                code: 'CABOFFER',
                description: 'Get Up-To Rs.200 Off On Outstation Cab Transactions'
            },
            {
                code: 'GODEAL',
                description: 'Get Up-To Rs.200 Off'
            }
        ],

        // Other Info
        zone: {
            id: zone._id,
            name: zone.zoneName
        },
        vendor: vehicle.vendor ? {
            id: vehicle.vendor._id,
            name: vehicle.vendor.vendorName
        } : null
    };
};

// ==========================================
// GROUPED SEARCH & VENDOR ASSIGNMENT
// ==========================================

/**
 * Find the best vendor for a booking based on zone and vehicle group/model
 * Selects vendor with the least active bookings who has available vehicles
 * @param {String} zoneId - Zone ID
 * @param {String} groupId - Vehicle Group ID
 * @param {String} modelId - Vehicle Model ID (optional, for more specific matching)
 * @returns {Object} - { vendor, availableVehicleCount }
 */
exports.findBestVendorForBooking = async (zoneId, groupId, modelId = null) => {
    // 1. Build vehicle query to find vendors with matching AVAILABLE vehicles
    const vehicleQuery = {
        zone: new mongoose.Types.ObjectId(zoneId),
        group: new mongoose.Types.ObjectId(groupId),
        operationalStatus: 'active',
        bookingStatus: 'available'  // Only count vehicles that are available for booking
    };

    if (modelId) {
        vehicleQuery.model = new mongoose.Types.ObjectId(modelId);
    }

    // 2. Get all vendors who have matching vehicles in this zone
    const vendorsWithVehicles = await Vehicle.aggregate([
        { $match: vehicleQuery },
        {
            $group: {
                _id: '$vendor',
                vehicleCount: { $sum: 1 }
            }
        }
    ]);

    if (vendorsWithVehicles.length === 0) {
        throw {
            message: 'No vendors available with vehicles for this selection in the zone',
            statusCode: 404
        };
    }

    const vendorIds = vendorsWithVehicles.map(v => v._id);

    // 3. Get blocked vendors (due limit exceeded)
    const blockedVendors = await Vendor.find({
        _id: { $in: vendorIds },
        isDeleted: false,
        $expr: { $lt: ['$wallet.balance', '$maxDueLimit'] }
    }).select('_id');
    const blockedVendorIds = blockedVendors.map(v => v._id.toString());

    // 4. Filter active vendors (exclude blocked)
    const activeVendors = await Vendor.find({
        _id: { $in: vendorIds },
        status: 'active',
        isDeleted: false
    }).select('_id vendorName operationalZone');

    const eligibleVendors = activeVendors.filter(
        v => !blockedVendorIds.includes(v._id.toString())
    );

    if (eligibleVendors.length === 0) {
        throw {
            message: 'No eligible vendors available for this booking',
            statusCode: 404
        };
    }

    // 5. Count  bookings per vendor
    // const activeStatuses = ['created', 'assigned', 'enRoute', 'arrived', 'inProgress'];
    const bookingCounts = await Booking.aggregate([
        {
            $match: {
                vendor: { $in: eligibleVendors.map(v => v._id) },
                // status: { $in: activeStatuses }
            }
        },
        { $group: { _id: '$vendor', count: { $sum: 1 } } }
    ]);

    const bookingCountMap = {};
    bookingCounts.forEach(b => {
        bookingCountMap[b._id.toString()] = b.count;
    });

    // 6. Find vendor with least bookings
    let bestVendor = null;
    let minBookings = Infinity;
    let vehicleCount = 0;

    for (const vendor of eligibleVendors) {
        const vendorBookings = bookingCountMap[vendor._id.toString()] || 0;
        if (vendorBookings < minBookings) {
            minBookings = vendorBookings;
            bestVendor = vendor;
            vehicleCount = vendorsWithVehicles.find(
                v => v._id.toString() === vendor._id.toString()
            )?.vehicleCount || 0;
        }
    }

    return {
        vendor: bestVendor,
        activeBookings: minBookings === Infinity ? 0 : minBookings,
        availableVehicleCount: vehicleCount
    };
};

/**
 * Search for available cab groups/models instead of individual vehicles
 * Returns unique group+model combinations with pricing, for Goibibo-style display
 * @param {Object} query - Search parameters
 * @returns {Object} - { groups: [...], total, zone, distance }
 */
exports.searchGroupedCabs = async (query) => {
    const {
        pickup, drop,
        tripType = 'oneWay',
        fuelType = 'petrol',
        isAc = true,
        scheduledTime,
        distance: queryDistance,
        rentalPackage
    } = query;

    const limit = Number(query.limit) || 20;
    const page = Number(query.page) || 1;

    // 1. Fetch active zones
    const zones = await Zone.find({ isActive: true }).lean();
    if (!zones.length) {
        return { groups: [], total: 0, pages: 0, page, zone: null };
    }

    // 2. Find the matching zone based on pickup coordinates
    let matchedZone = null;
    for (const zone of zones) {
        if (isPointInPolygon(pickup, zone.coordinates)) {
            const vehicleCount = await Vehicle.countDocuments({
                zone: zone._id,
                $or: [{ status: 'active' }, { operationalStatus: 'active' }]
            });
            if (vehicleCount > 0) {
                matchedZone = zone;
                break;
            }
        }
    }

    if (!matchedZone) {
        console.log('SearchGroupedCabs: No matching zone with vehicles found for coordinates', pickup);
        return { groups: [], total: 0, pages: 0, page, zone: null };
    }

    // 3. Calculate distance if not provided
    let distance = 0;
    let tripDuration = 0;

    // For rental trips, use rental package distance
    if (tripType === 'rental' && rentalPackage) {
        distance = rentalPackage.distance || 0;
        tripDuration = (rentalPackage.hours || 1) * 60; // Convert hours to minutes
    } else if (queryDistance && !isNaN(queryDistance)) {
        distance = parseFloat(queryDistance);
        tripDuration = Math.round((distance / 40) * 60);
    } else if (drop && !isNaN(drop.lat) && !isNaN(drop.lng)) {
        const result = await exports.calculateDistanceAsync(
            parseFloat(pickup.lat), parseFloat(pickup.lng),
            parseFloat(drop.lat), parseFloat(drop.lng)
        );
        distance = result.distance;
        tripDuration = result.duration;
    }

    // 4. Get blocked vendors
    const blockedVendors = await Vendor.find({
        isDeleted: false,
        $expr: { $lt: ['$wallet.balance', '$maxDueLimit'] }
    }).select('_id');
    const blockedVendorIds = blockedVendors.map(v => v._id);

    // 5. Aggregate unique group+fuelType combinations with all models and vendor counts
    const groupedResults = await Vehicle.aggregate([
        // Match active vehicles in zone
        {
            $match: {
                zone: matchedZone._id,
                $or: [{ status: 'active' }, { operationalStatus: 'active' }],
                vendor: { $nin: blockedVendorIds }
            }
        },
        // Group by group and fuelType, collect all unique models
        {
            $group: {
                _id: { group: '$group', fuelType: '$fuelType' },
                modelIds: { $addToSet: '$model' },
                vendorIds: { $addToSet: '$vendor' },
                vehicleCount: { $sum: 1 }
            }
        },
        // Only include combinations with at least one vendor
        { $match: { 'vendorIds.0': { $exists: true } } },
        // Sort by vehicle count descending
        { $sort: { vehicleCount: -1 } }
    ]);

    if (groupedResults.length === 0) {
        return { groups: [], total: 0, pages: 0, page, zone: matchedZone.zoneName, distance: 0 };
    }

    // 6. Populate group and model details
    const groupIds = [...new Set(groupedResults.map(r => r._id.group))];
    const allModelIds = [...new Set(groupedResults.flatMap(r => r.modelIds))];

    const [groups, models] = await Promise.all([
        VehicleGroup.find({ _id: { $in: groupIds } })
            .populate('category', 'categoryName defaultSeats')
            .lean(),
        VehicleModel.find({ _id: { $in: allModelIds } })
            .populate('brandName', 'brandName')
            .lean()
    ]);

    const groupMap = {};
    groups.forEach(g => { groupMap[g._id.toString()] = g; });

    const modelMap = {};
    models.forEach(m => { modelMap[m._id.toString()] = m; });

    // 7. Build final results with fare calculation
    const enrichedGroups = groupedResults.map(result => {
        const group = groupMap[result._id.group?.toString()] || {};
        const category = group.category || {};

        // Get all models for this group
        const resultModels = (result.modelIds || [])
            .map(mid => modelMap[mid?.toString()])
            .filter(m => m);

        // Use first model for primary display
        const primaryModel = resultModels[0] || {};
        const primaryBrand = primaryModel.brandName || {};

        // Use the vehicle's actual fuelType (from aggregation), not the query fuelType
        const vehicleFuelType = result._id.fuelType || fuelType;

        // Get pricing for the vehicle's fuel type
        const pricing = group.fuelPricing?.find(p => p.fuelType === vehicleFuelType)
            || group.fuelPricing?.[0] || {};

        // For rental trips, check if this group has matching rentalPricing
        let rentalOption = null;
        if (tripType === 'rental' && rentalPackage) {
            rentalOption = group.rentalPricing?.find(rp =>
                rp.hours === rentalPackage.hours &&
                (rp.fuelType === vehicleFuelType || !rp.fuelType || rp.fuelType === 'petrol')
            );
            // Also try matching just by hours if fuelType didn't match
            if (!rentalOption) {
                rentalOption = group.rentalPricing?.find(rp => rp.hours === rentalPackage.hours);
            }
        }

        // Calculate fare using existing function
        const fare = exports.calculateFare(tripType, {
            distance,
            stopsCount: 0,
            totalWaitTime: 0,
            rentalPackage: rentalPackage || null,
            group,
            zone: matchedZone,
            fuelType: vehicleFuelType,
            isAc,
            scheduledTime: scheduledTime ? new Date(scheduledTime) : new Date()
        });

        // Calculate discount
        const discountResult = vehicleService.calculateDiscountAmount(fare.total, group.discount);
        const discountAmount = discountResult.discountAmount || 0;
        const originalPrice = fare.total;
        const finalPrice = Math.max(0, fare.total - discountAmount);

        // Get model images (use first model's vehicleImages, not group's)
        const modelImages = primaryModel.vehicleImages || [];
        const frontImage = modelImages.find(img => img.view === 'front')?.url
            || modelImages[0]?.url
            || group.vehicleImages?.frontView
            || null;

        // Build model names array - comma separated display
        const modelNames = resultModels.map(m => {
            const name = m.modelName || '';
            return name;
        }).filter(n => n);

        // Use first model for primary display name, or group name
        const displayName = modelNames.join(', ') || group.groupName || 'Standard';

        // For rental, use rental package values for included KMs
        const includedKm = rentalOption ? rentalOption.baseKm : (pricing.baseKm || 0);
        const extraKmRate = rentalOption ? rentalOption.extraFarePerKm : (isAc ? (pricing.ratePerKmAc || 0) : (pricing.ratePerKmNonAc || 0));

        return {
            // IDs for booking (use first model's ID)
            groupId: group._id,
            modelId: primaryModel._id,

            // Display info (Goibibo style) - modelNames will show "Swift, Dzire, Amaze"
            displayName: displayName,
            modelNames: modelNames.length > 0 ? modelNames : [displayName],
            groupName: group.groupName,
            orSimilar: modelNames.length > 1,  // True if multiple models available

            // Category info
            category: category.categoryName || 'Standard',
            categoryId: category._id,

            // Vehicle details
            tier: group.tier || 'Economy',
            seats: group.seats || category.defaultSeats || 4,
            isAc: isAc,
            description: group.description || `${group.tier || 'Economy'} car`,

            // Images from MODEL (not group)
            images: modelImages,
            primaryImage: frontImage,

            // Fuel & Features
            fuelType: vehicleFuelType,
            bootSpace: pricing.bootSpace || 'available',
            luggageCapacity: pricing.luggageCapacity || 0,

            // Pricing
            fare: { ...fare, currency: 'INR' },
            includedKm,
            extraKmRate,

            // Discount
            discount: {
                hasDiscount: discountResult.isValid,
                discountType: discountResult.discountType || null,
                discountValue: discountResult.discountValue || 0,
                discountAmount: discountAmount,
                description: discountResult.description || null
            },
            originalPrice,
            finalPrice,
            taxesAndFees: fare.taxes || 0,

            // Cancellation & Reserve
            cancellationPolicy: group.cancellationPolicy || 'Free Cancellation Until 1 Hour Before Pickup',
            reserveAmount: Math.round(finalPrice * 0.2),

            // Availability
            vendorCount: result.vendorIds.length,
            vehicleCount: result.vehicleCount,

            // Zone Info
            zone: {
                id: matchedZone._id,
                name: matchedZone.zoneName
            },

            // Trip Details
            tripDetails: {
                distance: Math.round(distance * 10) / 10,
                estimatedDuration: tripDuration || Math.round((distance / 40) * 60)
            },

            // Rental-specific details
            rentalDetails: (tripType === 'rental' && rentalPackage) ? {
                packageHours: rentalPackage.hours,
                packageKm: rentalPackage.distance,
                includedKm: rentalOption?.baseKm || rentalPackage.distance,
                baseFare: rentalOption?.baseFare || fare.base,
                extraFarePerKm: rentalOption?.extraFarePerKm || extraKmRate,
                hasRentalPricing: !!rentalOption
            } : null
        };
    });

    // 8. Pagination
    const total = enrichedGroups.length;
    const startIndex = (page - 1) * limit;
    const paginatedGroups = enrichedGroups.slice(startIndex, startIndex + limit);

    return {
        groups: paginatedGroups,
        total,
        pages: Math.ceil(total / limit),
        page,
        zone: matchedZone.zoneName,
        zoneId: matchedZone._id,
        distance: Math.round(distance * 10) / 10,
        duration: tripDuration
    };
};



/**
 * Update pickup address for a booking
 * @param {String} bookingId - Booking ID
 * @param {Object} pickupData - New pickup location data {address, coordinates}
 */
exports.updatePickupAddress = async (bookingId, pickupData) => {
    const { address, coordinates } = pickupData;

    if (!address || !coordinates) {
        throw { message: 'Address and coordinates are required', statusCode: 400 };
    }

    const booking = await Booking.findById(bookingId);
    if (!booking) {
        throw { message: 'Booking not found', statusCode: 404 };
    }

    // Only allow update if booking is in created or payment_pending status
    if (!['created', 'payment_pending'].includes(booking.status)) {
        throw { message: 'Pickup address can only be updated before driver assignment', statusCode: 400 };
    }

    // Update pickup location
    booking.pickupLocation = {
        address,
        coordinates
    };

    await booking.save();

    // Add timeline entry
    await timelineService.addTimelineEntry(bookingId, {
        title: 'Pickup Address Updated',
        status: 'created',
        description: `Pickup address updated to: ${address}`
    });

    return booking;
};

/**
 * Update traveller details for a booking
 * @param {String} bookingId - Booking ID
 * @param {Object} travellerData - Traveller details {gender, fullName, phone, email}
 */
exports.updateTravellerDetails = async (bookingId, travellerData) => {
    const { gender, fullName, phone, email } = travellerData;

    // Validate required fields
    if (!fullName || !phone || !email) {
        throw { message: 'Full name, phone, and email are required', statusCode: 400 };
    }

    // Validate email format
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
        throw { message: 'Invalid email format', statusCode: 400 };
    }

    // Validate phone format (basic check for 10 digits)
    const phoneRegex = /^\d{10}$/;
    if (!phoneRegex.test(phone.replace(/\D/g, ''))) {
        throw { message: 'Phone number must be 10 digits', statusCode: 400 };
    }

    const booking = await Booking.findById(bookingId);
    if (!booking) {
        throw { message: 'Booking not found', statusCode: 404 };
    }

    // Update traveller details
    booking.travellerDetails = {
        gender: gender || null,
        fullName,
        phone,
        email
    };

    await booking.save();

    // Add timeline entry
    await timelineService.addTimelineEntry(bookingId, {
        title: 'Traveller Details Updated',
        status: booking.status,
        description: `Traveller details updated for: ${fullName}`
    });

    return booking;
};
