'use server';

import { z } from 'zod';
import { getDbClient } from '../dbClient';
import { BillingLocation, YearMonth } from '../db-types';
import { ObjectId } from 'mongodb';
import { withUser } from '@/app/lib/auth';
import { AuthenticatedUser } from '../types/next-auth';
import { gotoHome } from './navigationActions';
import { unstable_noStore as noStore } from 'next/cache';
import { asyncTimeout } from '../asyncTimeout';

export type State = {
    errors?: {
        locationName?: string[];
        locationNotes?: string[],
    };
    message?:string | null;
};

const FormSchema = z.object({
    _id: z.string(),
    locationName: z.coerce.string().min(1, "Location Name is required."),
    locationNotes: z.string(),
  });

const UpdateLocation = FormSchema.omit({ _id: true });

/**
 * Server-side action which adds or updates a bill
 * @param locationId location of the bill
 * @param prevState previous state of the form
 * @param formData form data
 * @returns 
 */
export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locationId: string | undefined, yearMonth: YearMonth | undefined, prevState:State, formData: FormData) => {

    noStore();

    const validatedFields = UpdateLocation.safeParse({
        locationName: formData.get('locationName'),
        locationNotes: formData.get('locationNotes'),
    });

    // If form validation fails, return errors early. Otherwise, continue...
    if(!validatedFields.success) {
        return({
            errors: validatedFields.error.flatten().fieldErrors,
            message: "Missing Fields",
        });
    }

    const {
        locationName,
        locationNotes,
    } = validatedFields.data;

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

    const { id: userId, email: userEmail } = user;

    if(locationId) {
        await dbClient.collection<BillingLocation>("lokacije").updateOne(
            {
                _id: locationId, // find a location with the given locationID
                userId // make sure the location belongs to the user
            },
            {
                $set: {
                    name: locationName,
                    notes: locationNotes,
                }
            });
    } else if(yearMonth) {
        await dbClient.collection<BillingLocation>("lokacije").insertOne({
            _id: (new ObjectId()).toHexString(),
            userId,
            userEmail,
            name: locationName,
            notes: locationNotes,
            yearMonth: yearMonth,
            bills: [],
        });
    }

    // await asyncTimeout(1000);

    if(yearMonth) await gotoHome(yearMonth);

    return {
        message: null,
        errors: undefined,
    };
});


export const fetchAllLocations = withUser(async (user:AuthenticatedUser, year:number) => {

    noStore();

    const dbClient = await getDbClient();

    const { id: userId } = user;

    // fetch all locations for the given year
    const locations = await dbClient.collection<BillingLocation>("lokacije")
        .find(
            {
                userId,
                "yearMonth.year": year,
            },
            {
                projection: {
                    // don't include the attachment binary data in the response
                    "bills.attachment.fileContentsBase64": 0
                },
            }
        )
        .sort({ 
            "yearMonth.year": -1,
            "yearMonth.month": -1,
            name: 1,
        })
        .toArray();

    // await asyncTimeout(1000);

    return(locations)
})

export const fetchLocationById = withUser(async (user:AuthenticatedUser, locationID:string) => {

    noStore();

    const dbClient = await getDbClient();

    const { id: userId } = user;

    // find a location with the given locationID
    const billLocation = await dbClient.collection<BillingLocation>("lokacije")
        .findOne(
            { _id: locationID, userId },
            {
                projection: {
                    // don't include the attachment binary data in the response
                    "bills.attachment.fileContentsBase64": 0,
                },
            }
        );

    if(!billLocation) {
        console.log(`Location ${locationID} not found`);
        return(null);
    }

    // await asyncTimeout(1000);

    return(billLocation);
})

export const deleteLocationById = withUser(async (user:AuthenticatedUser, locationID:string, yearMonth:YearMonth) => {

    noStore();

    const dbClient = await getDbClient();

    const { id: userId } = user;

    // find a location with the given locationID
    const post = await dbClient.collection<BillingLocation>("lokacije").deleteOne({ _id: locationID, userId });
    
    // await asyncTimeout(1000);

    await gotoHome(yearMonth)
})