'use server';

import { getDbClient } from '../dbClient';
import { ObjectId } from 'mongodb';
import { Bill, BillingLocation, YearMonth } from '../db-types';
import { AuthenticatedUser } from '../types/next-auth';
import { withUser } from '../auth';
import { unstable_noStore as noStore, unstable_noStore, revalidatePath } from 'next/cache';
import { getLocale } from 'next-intl/server';
import { gotoHomeWithMessage } from './navigationActions';

/**
 * Server-side action which adds a new month to the database
 * @param locationId location of the bill
 * @param billId ID of the bill
 * @param prevState previous state of the form
 * @param formData form data
 * @returns 
 */
export const addMonth = withUser(async (user:AuthenticatedUser, { year, month }: YearMonth) => {
    noStore();
    
    const { id: userId } = user;

    // update the bill in the mongodb
    const dbClient = await getDbClient();

    const prevYear = month === 1 ? year - 1 : year;
    const prevMonth = month === 1 ? 12 : month - 1;

    // find all locations for the previous month
    const prevMonthLocations = await dbClient.collection<BillingLocation>("lokacije").find({
        userId, // make sure that the locations belongs to the user
        yearMonth: {
            year: prevYear,
            month: prevMonth,
        }
    });

    const newMonthLocationsCursor = prevMonthLocations.map((prevLocation) => {
        return({
            // copy all the properties from the previous location
            ...prevLocation,
            // clear properties specific to the month
            seenByTenantAt: undefined,
            utilBillsProofOfPayment: undefined,
            // assign a new ID
            _id: (new ObjectId()).toHexString(),
            yearMonth: {
                year: year,
                month: month,
            },
            // copy bill array, but set all bills to unpaid and remove attachments and notes
            bills: prevLocation.bills.map((bill) => {
                return {
                    ...bill,
                    paid: false,
                    attachment: null,
                    notes: null,
                    payedAmount: null,
                    hub3aText: undefined,
                } as Bill
            })
        } as BillingLocation);
    });

    const newMonthLocations = await newMonthLocationsCursor.toArray()
    await dbClient.collection<BillingLocation>("lokacije").insertMany(newMonthLocations);
});

export const fetchAvailableYears = withUser(async (user:AuthenticatedUser) => {
    noStore();

    const { id: userId } = user;

    const dbClient = await getDbClient();

    // query mnogodb for all `yearMonth` values
    const years:number[] = await dbClient.collection<BillingLocation>("lokacije")
        .distinct("yearMonth.year", { userId })

    // sort the years in descending order
    const sortedYears = years.sort((a, b) => b - a);

    return(sortedYears);
})

/**
 * Fetches all locations for a specific month for the authenticated user
 * Only projects essential fields needed for the multi-bill-edit page
 * @param yearMonth - The year and month to fetch
 * @returns Array of locations with minimal bill data
 */
export const getLocationsByMonth = withUser(async (user: AuthenticatedUser, yearMonth: YearMonth) => {
    
    unstable_noStore();

    const { id: userId } = user;
    const dbClient = await getDbClient();

    // Use aggregation pipeline to calculate hasAttachment field
    const locations = await dbClient.collection<BillingLocation>("lokacije")
        .aggregate([
            {
                $match: {
                    userId,
                    yearMonth: {
                        year: yearMonth.year,
                        month: yearMonth.month,
                    }
                }
            },
            {
                $addFields: {
                    _id: { $toString: "$_id" },
                    bills: {
                        $map: {
                            input: "$bills",
                            as: "bill",
                            in: {
                                _id: { $toString: "$$bill._id" },
                                name: "$$bill.name",
                                paid: "$$bill.paid",
                                hasAttachment: { $ne: ["$$bill.attachment", null] },
                                proofOfPayment: "$$bill.proofOfPayment",
                            },
                        },
                    }
                }
            },
            {
                $project: {
                    "_id": 1,
                    "name": 1,
                    "yearMonth.year": 1,
                    "yearMonth.month": 1,
                    "bills._id": 1,
                    "bills.name": 1,
                    "bills.paid": 1,
                    "bills.hasAttachment": 1,
                    "bills.proofOfPayment.uploadedAt": 1,
                }
            },
            {
                $sort: {
                    name: 1,
                },
            },
        ])
        .toArray();

    return locations as Array<BillingLocation>;
});

/**
 * Updates the paid status of bills for locations in a specific month
 * @param yearMonth - The year and month to update
 * @param updates - Array of updates with locationId, billId, and paid status
 * @returns Success status
 */
export const updateMonth = withUser(async (
    user: AuthenticatedUser,
    yearMonth: YearMonth,
    updates: Array<{ locationId: string; billId: string; paid: boolean }>
) => {
    unstable_noStore();

    const { id: userId } = user;
    const dbClient = await getDbClient();

    // Group updates by location to minimize database operations
    const updatesByLocation = updates.reduce((acc, update) => {
        if (!acc[update.locationId]) {
            acc[update.locationId] = [];
        }
        acc[update.locationId].push(update);
        return acc;
    }, {} as Record<string, typeof updates>);

    // Perform bulk updates
    const updatePromises = Object.entries(updatesByLocation).map(
        async ([locationId, locationUpdates]) => {
            // For each bill update in this location
            const billUpdatePromises = locationUpdates.map(({ billId, paid }) =>
                dbClient.collection<BillingLocation>("lokacije").updateOne(
                    {
                        _id: locationId,
                        userId, // Ensure the location belongs to the authenticated user
                        yearMonth: {
                            year: yearMonth.year,
                            month: yearMonth.month,
                        },
                        'bills._id': billId,
                    },
                    {
                        $set: {
                            'bills.$.paid': paid,
                        },
                    }
                )
            );

            return Promise.all(billUpdatePromises);
        }
    );

    await Promise.all(updatePromises);

    // Revalidate the home page and multi-edit page to show fresh data
    revalidatePath('/home');
    revalidatePath(`/home/multi-bill-edit/${yearMonth.year}/${yearMonth.month}`);

    // Redirect to home page with year and month parameters, including success message
    if (yearMonth) {
        const locale = await getLocale();
        await gotoHomeWithMessage(locale, 'bill-multi-edit-saved', yearMonth);
    }

    return { success: true };
});
