/**
 * Zone Service
 * Business logic for zone operations
 */

const mongoose = require('mongoose');
const Zone = require('../models/zone.model');
const ZoneAdmin = require('../models/zone-admin.model');
const findUserInAllCollections = require('../helper/FindUserInAllCollections');
const Vendor = require('../models/vendor.model');
const Driver = require('../models/driver.model');
const Booking = require('../models/booking.model');
const { findUserAcrossCollections } = require('./auth.service');
const { sendMail, getConfig } = require('./email.service');
const { getEnv } = require('../utils/envLoader');

// ==========================================
// ANALYTICS SERVICES
// ==========================================

/**
 * Get zones overview statistics for super admin dashboard
 * Returns average metrics per zone
 */
exports.getZonesOverview = async () => {
    // Get total zone count
    const totalZones = await Zone.countDocuments();

    if (totalZones === 0) {
        return {
            totalZones: 0,
            avgAreaPerZone: 0,
            avgVendorsPerZone: 0,
            avgDriversPerZone: 0,
            avgOrdersPerZone: 0,
            avgRevenuePerZone: 0
        };
    }

    // Get all aggregated data in parallel
    const [
        totalVendors,
        totalDrivers,
        bookingStats
    ] = await Promise.all([
        Vendor.countDocuments({ isDeleted: false }),
        Driver.countDocuments(),
        Booking.aggregate([
            {
                $group: {
                    _id: null,
                    totalOrders: { $sum: 1 },
                    totalRevenue: {
                        $sum: {
                            $cond: [{ $eq: ['$status', 'completed'] }, '$fare.total', 0]
                        }
                    }
                }
            }
        ])
    ]);

    const totalOrders = bookingStats[0]?.totalOrders || 0;
    const totalRevenue = bookingStats[0]?.totalRevenue || 0;

    // Calculate zone-wise area (if coordinates are stored as polygon)
    // For now, we'll calculate based on zone coordinates if available
    let totalArea = 0;
    const zones = await Zone.find().select('coordinates');

    zones.forEach(zone => {
        if (zone.coordinates && zone.coordinates.length >= 3) {
            // Calculate polygon area using Shoelace formula (in km²)
            const area = calculatePolygonArea(zone.coordinates);
            totalArea += area;
            // console.log(totalArea)
        }
    });

    return {
        totalZones,
        avgAreaPerZone: totalZones > 0 ? Math.round((totalArea / totalZones) * 10) / 10 : 0,
        avgVendorsPerZone: totalZones > 0 ? Math.round(totalVendors / totalZones) : 0,
        avgDriversPerZone: totalZones > 0 ? Math.round(totalDrivers / totalZones) : 0,
        avgOrdersPerZone: totalZones > 0 ? Math.round(totalOrders / totalZones) : 0,
        avgRevenuePerZone: totalZones > 0 ? Math.round(totalRevenue / totalZones) : 0
    };
};

/**
 * Calculate polygon area using Shoelace formula
 * Converts lat/lng to approximate km²
 * @param {Array} coordinates - Array of {lat, lng} points
 */
function calculatePolygonArea(coordinates) {
    if (!coordinates || coordinates.length < 3) return 0;

    const n = coordinates.length;
    let area = 0;

    for (let i = 0; i < n; i++) {
        const j = (i + 1) % n;
        const lat1 = coordinates[i].lat || 0;
        const lng1 = coordinates[i].lng || 0;
        const lat2 = coordinates[j].lat || 0;
        const lng2 = coordinates[j].lng || 0;

        area += lat1 * lng2;
        area -= lat2 * lng1;
    }

    area = Math.abs(area) / 2;

    // Convert to km² (approximate: 1 degree ≈ 111 km)
    const kmPerDegree = 111;
    return area * kmPerDegree * kmPerDegree;
}

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

/**
 * Get zone assigned to admin user
 * @param {String} userId - Admin user ID
 */
exports.getAdminZone = async (userId) => {
    return await Zone.findOne({ zoneAdmin: userId });
};

/**
 * Get all zones with pagination
 * @param {Object} pagination - page, limit
 */
exports.getAllZones = async (pagination = {}) => {
    const { page = 1, limit = 10 } = pagination;
    const skip = (page - 1) * limit;

    const [zones, total] = await Promise.all([
        Zone.find()
            .populate('zoneAdmin', 'fullName email phone')
            .populate('createdBy', 'fullName email')
            .skip(skip)
            .limit(limit)
            .sort({ createdAt: -1 }),
        Zone.countDocuments()
    ]);

    // Get vendor count for each zone
    const zonesWithVendorCount = await Promise.all(
        zones.map(async (zone) => {
            const totalVendors = await Vendor.countDocuments({
                operationalZone: zone._id,  // Correct field name from Vendor model
                isDeleted: false
            });
            return {
                ...zone.toObject(),  // Convert Mongoose doc to plain object
                totalVendors
            };
        })
    );

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

/**
 * Get zone by ID
 * @param {String} zoneId - Zone ID
 */
exports.getZoneById = async (zoneId) => {
    const zone = await Zone.findById(zoneId)
        .populate('createdBy', 'fullName email')
        .populate('zoneAdmin', 'fullName email phone');

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

    return zone;
};

/**
 * Get all zones for dropdown
 */
exports.getAllZonesForDropdown = async () => {
    return Zone.find().select('zoneName zoneAddress');
};

/**
 * Get filtered zones with aggregation
 * @param {Array} filters - Array of filter conditions
 * @param {Object} pagination - page, limit
 */
exports.getFilteredZones = async (filters, pagination = {}) => {
    const { page = 1, limit = 10 } = pagination;
    const skip = (page - 1) * limit;

    const pipeline = [
        { $match: filters.length > 0 ? { $and: filters } : {} },
        {
            $lookup: {
                from: "zoneadmins",
                localField: "zoneAdmin",
                foreignField: "_id",
                as: "admin"
            }
        },
        { $unwind: { path: "$admin", preserveNullAndEmptyArrays: true } },
        {
            $project: {
                zoneName: 1,
                zoneAddress: 1,
                coordinates: 1,
                extraFareRule: 1,
                reasonOfFare: 1,
                isActive: 1,
                totalRatings: 1,
                createdAt: 1,
                zoneAdmin: {
                    _id: "$admin._id",
                    fullName: "$admin.fullName",
                    email: "$admin.email",
                    phone: "$admin.phone"
                }
            }
        },
        { $sort: { createdAt: -1 } },
        { $skip: skip },
        { $limit: limit }
    ];

    const [zones, total] = await Promise.all([
        Zone.aggregate(pipeline),
        Zone.countDocuments(filters.length > 0 ? { $and: filters } : {})
    ]);

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

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

/**
 * Create a new zone with admin
 * @param {Object} zoneData - Zone data
 * @param {String} createdBy - User ID creating the zone
 */
exports.createZone = async (zoneData, createdBy) => {
    const { zoneName, zoneAddress, coordinates, zoneAdmin, zoneContact, extraFareRule } = zoneData;

    // Check if admin already exists
    const existingAdminEmailInDB = await findUserInAllCollections(zoneAdmin.email);
    if (existingAdminEmailInDB) {
        await ZoneAdmin.findByIdAndDelete(existingAdminEmailInDB._id);
    }

    const existingZoneContact = await Zone.findOne({ zoneContact });
    if (existingZoneContact) {
        throw { message: 'Zone contact already exists', statusCode: 400 };
    }
    // Create zone admin
    const newZoneAdmin = await ZoneAdmin.create({
        fullName: zoneAdmin.fullName,
        email: zoneAdmin.email,
        phone: zoneAdmin.phone,
        password: zoneAdmin.password || "default123", // Should be changed on first login
        role: "zone-admin"
    });

    // Create zone

    try {
        const newZone = await Zone.create({
            zoneName,
            zoneAddress,
            coordinates,
            zoneContact,
            extraFareRule,
            zoneAdmin: newZoneAdmin._id,
            createdBy
        });

        // Send email to zone admin with login credentials
        const adminPassword = zoneAdmin.password || "default123";
        const emailConfig = await getConfig();
        const mailOptions = {
            from: emailConfig.user,
            to: zoneAdmin.email,
            subject: "Welcome to Cab Booking - Your Zone Admin Credentials",
            html: `
                <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
                    <h2 style="color: #333;">Welcome to Cab Booking Platform!</h2>
                    <p>Dear <strong>${zoneAdmin.fullName}</strong>,</p>
                    <p>You have been assigned as the Zone Admin for <strong>${zoneName}</strong>.</p>
                    <div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; margin: 20px 0;">
                        <h3 style="margin-top: 0; color: #555;">Your Login Credentials:</h3>
                        <p><strong>Email:</strong> ${zoneAdmin.email}</p>
                        <p><strong>Password:</strong> ${adminPassword}</p>
                        <p><strong>Login URL:</strong> <a href="${getEnv("FRONTEND_URL") || process.env.FRONTEND_URL}/login">${getEnv("FRONTEND_URL") || process.env.FRONTEND_URL}/login</a></p>
                    </div>
                    <p style="color: #e74c3c;"><strong>Important:</strong> Please change your password after your first login for security purposes.</p>
                    <p>If you have any questions, please contact the support team.</p>
                    <br>
                    <p>Best regards,<br>Cab Booking Team</p>
                </div>
            `
        };

        // Send email asynchronously (don't block the response)
        sendMail(mailOptions)
            .then(() => console.log(`Welcome email sent to zone admin: ${zoneAdmin.email}`))
            .catch((err) => console.error(`Failed to send welcome email to zone admin: ${err.message}`));

        return { zone: newZone, zoneAdmin: newZoneAdmin };
    } catch (error) {
        await ZoneAdmin.findByIdAndDelete(newZoneAdmin._id);
        throw error;
    }
};

/**
 * Update zone by ID
 * @param {String} zoneId - Zone ID
 * @param {Object} updates - Fields to update
 */
exports.updateZone = async (zoneId, updates) => {
    const { zoneName, zoneAddress, coordinates, zoneContact, extraFareRule } = updates;

    const zone = await Zone.findById(zoneId);
    if (!zone) {
        throw { message: 'Zone not found', statusCode: 404 };
    }

    if (zoneName) zone.zoneName = zoneName;
    if (zoneAddress) {
        if (zoneAddress.address) zone.zoneAddress.address = zoneAddress.address;
        if (zoneAddress.city) zone.zoneAddress.city = zoneAddress.city;
        if (zoneAddress.state) zone.zoneAddress.state = zoneAddress.state;
    }
    if (coordinates && coordinates.length > 2) zone.coordinates = coordinates;
    if (zoneContact) zone.zoneContact = zoneContact;
    if (extraFareRule) {
        if (extraFareRule.fareType) zone.extraFareRule.fareType = extraFareRule.fareType;
        if (extraFareRule.fareAmount !== undefined) zone.extraFareRule.fareAmount = extraFareRule.fareAmount;
        if (extraFareRule.reason) zone.extraFareRule.reason = extraFareRule.reason;
    }

    await zone.save();

    return Zone.findById(zoneId)
        .populate('zoneAdmin', 'fullName email phone')

};

/**
 * Update zone admin
 * @param {String} zoneId - Zone ID
 * @param {Object} adminData - Admin data to update
 */
exports.updateZoneAdmin = async (zoneId, adminData) => {
    const { name, email, phone, password } = adminData;

    if (!name || !email || !phone) {
        throw { message: 'All fields are required', statusCode: 400 };
    }

    const zone = await Zone.findById(zoneId);
    if (!zone) {
        throw { message: 'Zone not found', statusCode: 404 };
    }

    // Delete old admin if exists
    if (zone.zoneAdmin) {
        await ZoneAdmin.findByIdAndDelete(zone.zoneAdmin);
    }

    // Check if new email already exists
    const existingAdmin = await findUserAcrossCollections(email);
    // console.log(existingAdmin)
    if (existingAdmin.user) {
        throw { message: 'Email already registered', statusCode: 400 };
    }

    // Create new admin
    const newZoneAdmin = await ZoneAdmin.create({
        fullName: name,
        email,
        phone,
        password: password || undefined,
        role: "zone-admin"
    });

    zone.zoneAdmin = newZoneAdmin._id;
    await zone.save();

    return { zone, zoneAdmin: newZoneAdmin };
};

/**
 * Delete zone by ID
 * @param {String} zoneId - Zone ID
 */
exports.deleteZone = async (zoneId) => {
    const zone = await Zone.findById(zoneId);
    if (!zone) {
        throw { message: 'Zone not found', statusCode: 404 };
    }

    // Delete associated admin
    if (zone?.zoneAdmin) {
        await ZoneAdmin.findByIdAndDelete(zone.zoneAdmin);
    }

    await Zone.findByIdAndDelete(zoneId);
    return zone;
};

/**
 * Change zone status (activate/deactivate)
 * @param {String} zoneId - Zone ID
 * @param {Boolean} isActive - New status
 */
exports.changeZoneStatus = async (zoneId, isActive) => {
    if (typeof isActive !== 'boolean') {
        throw { message: 'isActive must be a boolean value', statusCode: 400 };
    }

    const zone = await Zone.findById(zoneId);
    if (!zone) {
        throw { message: 'Zone not found', statusCode: 404 };
    }

    const zoneAdmin = await ZoneAdmin.findById(zone.zoneAdmin);
    if (zoneAdmin) {
        zoneAdmin.isActive = isActive;
        await zoneAdmin.save();
    }
    zone.isActive = isActive;
    await zone.save();

    return zone;
};

/**
 * Get detailed zone analytics
 * @param {String} zoneId - Zone ID
 * @param {Object} filters - Date filters
 */
exports.getZoneAnalytics = async (zoneId, filters = {}) => {
    const { startDate, endDate } = filters;

    // Build date filter
    const dateFilter = {};
    if (startDate && endDate) {
        dateFilter.createdAt = {
            $gte: new Date(startDate),
            $lte: new Date(new Date(endDate).setHours(23, 59, 59, 999))
        };
    } else {
        // Default to last 30 days if no date range provided
        const thirtyDaysAgo = new Date();
        thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
        dateFilter.createdAt = { $gte: thirtyDaysAgo };
    }

    // Get zone details
    const zone = await Zone.findById(zoneId);
    if (!zone) {
        throw { message: 'Zone not found', statusCode: 404 };
    }

    const { ObjectId } = mongoose.Types;
    const zoneObjectId = new ObjectId(zoneId);

    // 1. Total Orders count
    const totalBookings = await Booking.countDocuments({
        zone: zoneObjectId,
        ...dateFilter
    });

    // 2. Completed Orders count
    const completedBookings = await Booking.countDocuments({
        zone: zoneObjectId,
        status: 'completed',
        ...dateFilter
    });

    // 3. Cancelled Orders count
    const cancelledBookings = await Booking.countDocuments({
        zone: zoneObjectId,
        status: 'cancelled',
        ...dateFilter
    });

    // 3a. Total Vendors in Zone
    const totalVendorsCount = await Vendor.countDocuments({
        operationalZone: zoneObjectId
    });

    // 3b. Total Drivers in Zone
    const totalDriversCount = await Driver.countDocuments({
        zone: zoneObjectId
    });

    // 4. Total Revenue (sum of fare.total for completed trips)
    const revenueAggregation = await Booking.aggregate([
        {
            $match: {
                zone: zoneObjectId,
                status: 'completed',
                ...dateFilter
            }
        },
        {
            $group: {
                _id: null,
                totalRevenue: { $sum: "$fare.total" }
            }
        }
    ]);
    const totalRevenue = revenueAggregation.length > 0 ? revenueAggregation[0].totalRevenue : 0;

    // 5. Orders by Status for Chart
    const ordersByStatusAggregation = await Booking.aggregate([
        {
            $match: {
                zone: zoneObjectId,
                ...dateFilter
            }
        },
        {
            $group: {
                _id: "$status",
                count: { $sum: 1 }
            }
        }
    ]);

    // Map aggregation result to chart format
    // Defines colors for each status
    const statusColors = {
        completed: '#34D399', // Green
        cancelled: '#FCA5A5', // Red
        created: '#60A5FA',   // Blue
        assigned: '#60A5FA',  // Blue (same as created for simplified view)
        driver_assigned: '#60A5FA',
        arrived: '#FACC15',   // Yellow
        in_progress: '#FACC15',
        started: '#FACC15'
    };

    // Helper map for frontend display names
    const statusMap = {
        completed: 'Completed',
        cancelled: 'Cancelled',
        created: 'Created',
        assigned: 'Assigned',
        arrived: 'Arrived',
        in_progress: 'In Progress'
    };

    const ordersByStatus = ordersByStatusAggregation.map(item => {
        const percentage = totalBookings > 0
            ? ((item.count / totalBookings) * 100).toFixed(1)
            : 0;

        return {
            name: statusMap[item._id] || item._id, // Fallback to raw status if not in map
            value: item.count,
            color: statusColors[item._id] || '#9CA3AF', // Gray default
            percentage: percentage
        };
    });

    // 6. Top Vendors by Revenue
    // Join Bookings with Vendors to get names
    const topVendorsAggregation = await Booking.aggregate([
        {
            $match: {
                zone: zoneObjectId,
                status: 'completed',
                vendor: { $exists: true, $ne: null }, // Ensure vendor exists
                ...dateFilter
            }
        },
        {
            $group: {
                _id: "$vendor",
                orders: { $sum: 1 },
                revenue: { $sum: "$fare.total" }
                // Note: Average rating would require joining with Driver or Review model
            }
        },
        {
            $sort: { revenue: -1 } // Sort by highest revenue
        },
        {
            $limit: 5 // Top 5
        },
        {
            $lookup: {
                from: "vendors", // Collection name in MongoDB
                localField: "_id",
                foreignField: "_id",
                as: "vendorDetails"
            }
        },
        {
            $unwind: "$vendorDetails"
        },
        {
            $project: {
                _id: 1,
                vendorId: "$_id",
                name: "$vendorDetails.vendorName",
                orders: 1,
                revenue: 1,
                // Adding a placeholder rating or fetching if available in vendor model
                rating: { $ifNull: ["$vendorDetails.rating", 4.5] }
            }
        }
    ]);

    // Format for frontend
    const topVendors = topVendorsAggregation.map((vendor, index) => ({
        rank: index + 1,
        vendorId: vendor.vendorId,
        name: vendor.name,
        orders: vendor.orders,
        revenue: vendor.revenue,
        rating: vendor.rating
    }));

    return {
        summary: {
            totalOrders: totalBookings,
            totalRevenue: totalRevenue,
            completedOrders: completedBookings,
            cancelledOrders: cancelledBookings,
            totalVendors: totalVendorsCount,
            totalDrivers: totalDriversCount
        },
        ordersByStatus,
        topVendors,
        dateRange: {
            startDate: startDate || new Date(new Date().setDate(new Date().getDate() - 30)).toISOString().split('T')[0],
            endDate: endDate || new Date().toISOString().split('T')[0]
        }
    };
};

/**
 * Build filters for zone search
 * @param {Object} queryParams - Query parameters
 */
exports.buildZoneFilters = async (queryParams) => {
    const filters = [];
    const { startDate, endDate, zoneName, isActive, minFare, maxFare, minRating, zoneAdmin, city } = queryParams;

    if (startDate && endDate) {
        filters.push({
            createdAt: {
                $gte: new Date(startDate),
                $lte: new Date(new Date(endDate).setHours(23, 59, 59, 999))
            }
        });
    }

    if (zoneName) {
        filters.push({ zoneName: { $regex: zoneName, $options: 'i' } });
    }

    // City filter - searches in zoneAddress.city
    if (city) {
        filters.push({ 'zoneAddress.city': { $regex: city, $options: 'i' } });
    }

    if (isActive !== undefined) {
        filters.push({ isActive: isActive === 'true' });
    }

    if (minFare || maxFare) {
        if (minFare > maxFare) {
            return { message: 'minFare should be less than maxFare', statusCode: 400 };
        }
        const fareFilter = {};
        if (minFare) {
            fareFilter.$gte = Number(minFare);
        }
        if (maxFare) {
            fareFilter.$lte = Number(maxFare);
        }
        filters.push({ 'extraFareRule.fareAmount': fareFilter });
    }

    if (minRating) {
        filters.push({ totalRatings: { $gte: Number(minRating) } });
    }

    if (zoneAdmin) {
        const zoneAdminForFilter = await ZoneAdmin.findOne({ fullName: { $regex: zoneAdmin, $options: 'i' } });
        if (zoneAdminForFilter) {
            filters.push({ zoneAdmin: zoneAdminForFilter._id });
        } else {
            // If no admin found with that name, return empty result by using impossible condition
            filters.push({ zoneAdmin: null });
        }
    }

    return filters;
};

/**
 * Get orders by status for zone
 * @param {String} zoneId - Zone ID
 */
exports.getOrdersByStatusForZone = async (zoneId) => {
    const zone = await Zone.findById(zoneId);
    if (!zone) {
        throw { message: 'Zone not found', statusCode: 404 };
    }

    const ordersCountByStatus = await Booking.aggregate([
        {
            $match: {
                zone: zone._id
            }
        },
        {
            $group: {
                _id: '$status',
                count: { $sum: 1 }
            }
        }
    ]);
    const ordersCount = await Booking.countDocuments({ zone: zone._id });
    const ordersPercentage = { created: 0, assigned: 0, enRoute: 0, arrived: 0, inProgress: 0, completed: 0, cancelled: 0 };

    ordersCountByStatus.forEach(order => {
        ordersPercentage[order._id] = ((order.count / ordersCount) * 100).toFixed(2);
    });

    return ordersPercentage;
};
