import dbConnect from "@/database/dbConnect";
import { NextResponse } from "next/server";
import memberModel from "@/database/models/memberModel";
import serviceModel from "@/database/models/serviceModel";
import mongoose from 'mongoose'
import statisticsModel from "@/database/models/statisticsModel";
import incomeModel from "@/database/models/incomeModel";

// POST METHOD
export async function POST(req:Request)
{
    try{
        // connect to the db
        dbConnect();
        // get the request payload
        const { payload } = await req.json();
        // declare the needed variables
        const firstName : string | null = payload.firstName as string | null,
        lastName : string | null = payload.lastName as string | null,
        email : string | null = payload.email as string | null,
        phone : string | null = payload.phone as string | null,
        address : string | null = payload.address as string | null,
        // Parse and validate services
        services : string[] = (() => {
            try {
                if (typeof payload.services === 'string') {
                    const parsed = JSON.parse(payload.services);
                    if (!Array.isArray(parsed)) {
                        throw new Error('Services must be an array');
                    }
                    return parsed;
                } else if (Array.isArray(payload.services)) {
                    return payload.services;
                } else {
                    throw new Error('Services must be an array or stringified array');
                }
            } catch (parseError) {
                console.error('Error parsing services:', parseError);
                console.error('Services value:', payload.services);
                console.error('Services type:', typeof payload.services);
                return [];
            }
        })(),
        planDelay : number = payload.planDelay ? payload.planDelay : 1 as number,
        gendre : 'm' | 'w' = payload.gendre as 'm' | 'w',
        bodyState : {
            startBodyForm: String,
            startWeight: Number,
        } | null = payload.bodyState as {
            startBodyForm: String,
            startWeight: Number,
        } | null,
        subscriptionDate : string | null = payload.subscriptionDate as string | null;
        
        // Validate required fields
        if (!firstName || !lastName || !phone) {
            return NextResponse.json({
                success: false,
                message: "Missing required fields: firstName, lastName, and phone are required",
            }, {
                status: 400,
                headers: {
                    "content-type": "application/json"
                }
            })
        }
        
        // Validate gendre field
        if (gendre && gendre !== 'm' && gendre !== 'w') {
            return NextResponse.json({
                success: false,
                message: "Invalid gender value. Must be 'm' or 'w'",
            }, {
                status: 400,
                headers: {
                    "content-type": "application/json"
                }
            })
        }
        
        // Validate services array
        if (!services || services.length === 0) {
            return NextResponse.json({
                success: false,
                message: "At least one service must be selected",
            }, {
                status: 400,
                headers: {
                    "content-type": "application/json"
                }
            })
        }
        // Get service details and create per-service subscription data
        const ids = services?.map((v , i) => {
            return new mongoose.Types.ObjectId(v as any)
        })
        const servicesDocs = await serviceModel.find({ _id: { $in: ids } })
        
        // Validate that all services exist
        if (servicesDocs.length !== services.length) {
            return NextResponse.json({
                success: false,
                message: "One or more selected services do not exist",
            }, {
                status: 400,
                headers: {
                    "content-type": "application/json"
                }
            })
        }
        
        // Calculate total monthly pay
        const payMonth = servicesDocs.reduce((total, service) => total + service.price, 0)
        
        // current time variables - use custom date if provided, otherwise use current date
        const subscription_date = subscriptionDate ? new Date(subscriptionDate) : new Date();
        const subscription_date_unix = Math.floor(subscription_date.getTime() / 1000);
        const planDelay_unix = planDelay * 2592000;
        const planExpAt = new Date((subscription_date_unix + planDelay_unix) * 1000);
        
        // Create services array with per-service subscription details
        const servicesArray = servicesDocs.map(service => ({
            serviceID: service._id.toString(),
            serviceName: service.name,
            registeredAt: subscription_date.toUTCString(),
            registeredAt_unix: subscription_date_unix,
            planDelay: planDelay,
            planDelay_unix: planDelay_unix,
            planExpAt: planExpAt.toUTCString(),
            planExpAt_unix: subscription_date_unix + planDelay_unix,
            planUpdatedAt: subscription_date.toUTCString(),
            planUpdatedAt_unix: subscription_date_unix,
            active: true,
        }))
        
        // Debug logging
        // Debug: Log services parsing
        console.log('Original services from payload:', payload.services);
        console.log('Parsed services:', services);
        
        // Increment the value of totalSubscribers for all included services
        await serviceModel.updateMany({_id : {$in : ids}} , {
            $inc: {
                totalSubscribers: 1
            }
        })
        
        // Save the doc with new structure
        const doc = new memberModel({
            firstName,
            lastName,
            email,
            phone,
            address,
            payMonth,
            services: servicesArray,
            gendre,
            bodyState,
            registerAt: subscription_date.toUTCString(),
            registerAt_unix: subscription_date_unix,
            // Global subscription status (derived from services)
            planUpdatedAt: subscription_date.toUTCString(),
            planUpdatedAt_unix: subscription_date_unix,
            planDelay,
            planDelay_unix: planDelay_unix,    
            planExpAt: planExpAt.toUTCString(),
            planExpAt_unix: subscription_date_unix + planDelay_unix,
            active: true,
        })
        
        await doc.save()

        // Create income entry for new member subscription
        if (payMonth > 0) {
            try {
                const serviceNames = await serviceModel.find({ _id: { $in: ids } }, 'name');
                const serviceNamesList = serviceNames.map(service => service.name).join(', ');
                const totalAmount = payMonth * planDelay;
                
                const incomeEntry = new incomeModel({
                    name: `New Member Subscription - ${firstName} ${lastName}`,
                    description: `New member subscription for services: ${serviceNamesList} (${planDelay} month${planDelay > 1 ? 's' : ''})`,
                    amount: totalAmount,
                    addedAt: subscription_date_unix
                });
                await incomeEntry.save();
                
                // Update total income in statistics
                await statisticsModel.findOneAndUpdate({}, {
                    $inc: {
                        totalIncome: totalAmount
                    }
                }, { upsert: true });
            } catch (incomeError) {
                console.error('Error creating income entry:', incomeError);
                // Don't fail the member creation if income entry fails
            }
        }

        // return the success response with the new added doc
        return NextResponse.json({
            success: true,
            message: "requestTerminatedWithSuccess",
            data: doc,
        }, {
            status: 200,
            headers: {
                "content-type": "application/json"
            }
        })
    }catch(e)
    {
        // Log the actual error for debugging
        console.error('Error adding new member:', e);
        
        // catch any error and return an error response
        return NextResponse.json({
            success: false,
            message: "serverError",
            error: e instanceof Error ? e.message : 'Unknown error'
        }, {
            status: 500,
            headers: {
                "content-type": "application/json"
            }
        })
    }
}
// set the revalidate variable
export const revalidate = 5;
// GET METHOD
export async function GET(req:Request)
{
    try{
        // connect to the db
        dbConnect();
        // get the parms
        const { searchParams } = new URL(req.url)
        const type = searchParams.get('type')
        // if get by page
        if(type=='page')
        {
            // get the page
            const page : string | null = searchParams.get('page') ? searchParams.get('page') : '0',
            range : number[] = [(parseInt(page?page : '0') - 1) * 20, 20];
            // get the docs
            const docs = await memberModel.find({}).skip(range[0]).limit(range[1]),
            // get the size of the docs
            docs_count = await memberModel.countDocuments({});
            // prepare the return data
            return NextResponse.json({
                success: true,
                message: "docsGetedSuccessfully",
                data: {
                    docs,
                    docs_count,
                }
            })
        }
        // if type is search
        else if(type == 'search')
        {
            // get the searchKeyword param
            let searchKeyword = searchParams.get('searchKeyword')
            // get the search results docs
            let results = await memberModel.find({
                $or: [
                    // search by ( case insensitive search )
                    { firstName: { $regex: searchKeyword, $options: 'i' } },
                    { lastName: { $regex: searchKeyword, $options: 'i' } },
                    { email: { $regex: searchKeyword, $options: 'i' } },
                    { phone: { $regex: searchKeyword, $options: 'i' } },
                ]
            })
            // get the docs
            let responseData = {
                docs: results,
                docs_count : results.length,
            }
            // return the response
            return NextResponse.json({
                success: true,
                message: "docsGetedSuccessfully",
                data: responseData ? responseData : [],
            } , {
                status: 200,
            })
        }
    }catch(e)
    {
        // Log the actual error for debugging
        console.error('Error updating member:', e);
        
        // catch any error and return an error response
        return NextResponse.json({
            success: false,
            message: "serverError",
            error: e instanceof Error ? e.message : 'Unknown error'
        }, {
            status: 500,
            headers: {
                "content-type": "application/json"
            }
        })
    }
}
// DELETE METHOD
export async function DELETE(req:Request)
{
    try{
        // connect to the db
        dbConnect();
        // get the parms
        const { searchParams } = new URL(req.url)
        const page = searchParams.get('page'),        
        _id : string | null = searchParams.get('_id'),
        range : number[] = [(parseInt(page?page : '0') - 1) * 20, 20];
        // delete the doc
        await memberModel.findByIdAndRemove(_id)
        // get the docs by page
        const docs = await memberModel.find({}).skip(range[0]).limit(range[1]),
        // get the size of the docs
        docs_count = await memberModel.countDocuments({});
        // prepare the return data
        return NextResponse.json({
            success: true,
            message: "docsGetedSuccessfully",
            data: {
                docs,
                docs_count,
            }
        })
    }catch(e)
    {
        // Log the actual error for debugging
        console.error('Error in GET request:', e);
        
        // catch any error and return an error response
        return NextResponse.json({
            success: false,
            message: "serverError",
            error: e instanceof Error ? e.message : 'Unknown error'
        }, {
            status: 500,
            headers: {
                "content-type": "application/json"
            }
        })
    }
}
// PUT METHOD
export async function PUT(req:Request)
{
    try{
        // connect to the db
        dbConnect();
        // get the request payload
        const { args , payload } = await req.json();
        // check the type
        const type : "personal" | "subscription" | null = args.type as "personal" | "subscription" | null;
        if(type == "personal")
        {
            // declare the needed variables
            const _id : string | null = payload._id as string | null,
            firstName : string | null = payload.firstName as string | null,
            lastName : string | null = payload.lastName as string | null,
            email : string | null = payload.email as string | null,
            phone : string | null = payload.phone as string | null,
            address : string | null = payload.address as string | null,
            active : boolean | null = payload.active as boolean | null,
            gendre : 'm' | 'w' | null = payload.gendre as 'm' | 'w' | null;
            // save the doc
            const doc = await memberModel.findByIdAndUpdate( _id , {
                firstName,
                lastName,
                email,
                phone,
                address,
                active,
                gendre,
            } , {
                new: true
            })
            // return the success response
            return NextResponse.json({
                success: true,
                message: "requestTerminatedWithSuccess",
                data: doc,
            }, {
                status: 200,
                headers: {
                    "content-type": "application/json"
                }
            })
        }       
        else if(type == "subscription")
        {
            // declare the needed variables
            const _id : string | null = payload._id as string | null,
            // Parse services if it's a string (handle both string and array cases)
            newServices : string[] = (() => {
                if (typeof payload.services === 'string') {
                    try {
                        return JSON.parse(payload.services);
                    } catch (parseError) {
                        console.error('Error parsing services JSON in PUT:', parseError);
                        return [];
                    }
                }
                return payload.services as string[];
            })(),
            planDelay : number = payload.planDelay ? payload.planDelay : 1 as number,
            subscriptionDate : string | null = payload.subscriptionDate as string | null,
            bodyState : {
                currentBodyForm: String,
                currentWeight: Number,
            } | null = payload.bodyState as {
                currentBodyForm: String,
                currentWeight: Number,
            } | null;

            // Get current member data
            const currentMember = await memberModel.findById(_id);
            if (!currentMember) {
                return NextResponse.json({
                    success: false,
                    message: "Member not found",
                }, {
                    status: 404,
                    headers: {
                        "content-type": "application/json"
                    }
                })
            }

            // Get service details for new services
            const newServiceIds = newServices.map(id => new mongoose.Types.ObjectId(id as string));
            const newServicesDocs = await serviceModel.find({ _id: { $in: newServiceIds } });
            
            // Current time variables - use custom date if provided, otherwise use current date
            const current_date = subscriptionDate ? new Date(subscriptionDate) : new Date();
            const current_date_unix = Math.floor(current_date.getTime() / 1000);
            const planDelay_unix = planDelay * 2592000;
            
            // Process current services and new services
            const currentServices = currentMember.services || [];
            const updatedServices = [...currentServices];
            let totalIncome = 0;
            const incomeServiceNames = [];
            
            // Handle each new service
            for (const newServiceDoc of newServicesDocs) {
                const serviceId = newServiceDoc._id.toString();
                const existingServiceIndex = updatedServices.findIndex(s => s.serviceID === serviceId);
                
                if (existingServiceIndex >= 0) {
                    // Service exists - extend or replace based on current status
                    const existingService = updatedServices[existingServiceIndex];
                    const isServiceActive = existingService.planExpAt_unix > current_date_unix;
                    
                    if (isServiceActive) {
                        // Extend active service
                        const newExpiration = existingService.planExpAt_unix + planDelay_unix;
                        updatedServices[existingServiceIndex] = {
                            serviceID: existingService.serviceID,
                            serviceName: existingService.serviceName,
                            registeredAt: existingService.registeredAt,
                            registeredAt_unix: existingService.registeredAt_unix,
                            planDelay: existingService.planDelay + planDelay,
                            planDelay_unix: existingService.planDelay_unix + planDelay_unix,
                            planExpAt: new Date(newExpiration * 1000).toUTCString(),
                            planExpAt_unix: newExpiration,
                            planUpdatedAt: current_date.toUTCString(),
                            planUpdatedAt_unix: current_date_unix,
                            active: true,
                        };
                        // Charge for extension
                        totalIncome += newServiceDoc.price * planDelay;
                        incomeServiceNames.push(`${newServiceDoc.name} (extended)`);
                    } else {
                        // Replace expired service - keep original registration date
                        const newExpiration = current_date_unix + planDelay_unix;
                        updatedServices[existingServiceIndex] = {
                            serviceID: existingService.serviceID,
                            serviceName: existingService.serviceName,
                            registeredAt: existingService.registeredAt,
                            registeredAt_unix: existingService.registeredAt_unix,
                            planDelay: planDelay,
                            planDelay_unix: planDelay_unix,
                            planExpAt: new Date(newExpiration * 1000).toUTCString(),
                            planExpAt_unix: newExpiration,
                            planUpdatedAt: current_date.toUTCString(),
                            planUpdatedAt_unix: current_date_unix,
                            active: true,
                        };
                        // Charge for replacement
                        totalIncome += newServiceDoc.price * planDelay;
                        incomeServiceNames.push(`${newServiceDoc.name} (renewed)`);
                    }
                } else {
                    // New service - add it
                    const newExpiration = current_date_unix + planDelay_unix;
                    updatedServices.push({
                        serviceID: serviceId,
                        serviceName: newServiceDoc.name,
                        registeredAt: current_date.toUTCString(),
                        registeredAt_unix: current_date_unix,
                        planDelay: planDelay,
                        planDelay_unix: planDelay_unix,
                        planExpAt: new Date(newExpiration * 1000).toUTCString(),
                        planExpAt_unix: newExpiration,
                        planUpdatedAt: current_date.toUTCString(),
                        planUpdatedAt_unix: current_date_unix,
                        active: true,
                    });
                    // Charge for new service
                    totalIncome += newServiceDoc.price * planDelay;
                    incomeServiceNames.push(`${newServiceDoc.name} (new)`);
                }
            }
            
            // Calculate total monthly pay and global subscription status
            const allServiceIds = updatedServices.map(s => new mongoose.Types.ObjectId(s.serviceID));
            const allServicesDocs = await serviceModel.find({ _id: { $in: allServiceIds } });
            const payMonth = allServicesDocs.reduce((total, service) => total + service.price, 0);
            
            // Determine global subscription status (latest expiration date)
            const latestExpiration = Math.max(...updatedServices.map(s => s.planExpAt_unix));
            const globalActive = latestExpiration > current_date_unix;
            const globalPlanDelay = Math.max(...updatedServices.map(s => s.planDelay));
            
            // Convert services to plain objects to avoid Mongoose document nesting issues
            const plainServices = updatedServices.map(service => ({
                serviceID: service.serviceID,
                serviceName: service.serviceName,
                registeredAt: service.registeredAt,
                registeredAt_unix: service.registeredAt_unix,
                planDelay: service.planDelay,
                planDelay_unix: service.planDelay_unix,
                planExpAt: service.planExpAt,
                planExpAt_unix: service.planExpAt_unix,
                planUpdatedAt: service.planUpdatedAt,
                planUpdatedAt_unix: service.planUpdatedAt_unix,
                active: service.active,
            }));
            
            // Update member document
            const doc = await memberModel.findByIdAndUpdate(_id, {
                bodyState,
                services: plainServices,
                payMonth,
                planUpdatedAt: current_date.toUTCString(),
                planUpdatedAt_unix: current_date_unix,
                planDelay: globalPlanDelay,
                planDelay_unix: globalPlanDelay * 2592000,
                planExpAt: new Date(latestExpiration * 1000).toUTCString(),
                planExpAt_unix: latestExpiration,
                active: globalActive,
            }, {
                new: true
            });
            
            // Add income entry if there's any charge
            if (totalIncome > 0) {
                await statisticsModel.findOneAndUpdate({}, {
                    $inc: {
                        totalIncome: totalIncome
                    }
                });
                
                const member = await memberModel.findById(_id, 'firstName lastName');
                const incomeEntry = new incomeModel({
                    name: `Subscription Update - ${member?.firstName} ${member?.lastName}`,
                    description: `Services updated: ${incomeServiceNames.join(', ')}`,
                    amount: totalIncome,
                    addedAt: current_date_unix
                });
                await incomeEntry.save();
            }
            
            // return the success response
            return NextResponse.json({
                success: true,
                message: "requestTerminatedWithSuccess",
                data: doc,
            }, {
                status: 200,
                headers: {
                    "content-type": "application/json"
                }
            })
        }
    }catch(e)
    {
        // catch any error and return an error response
        return NextResponse.json({
            success: false,
            message: "serverError",
        }, {
            status: 500,
            headers: {
                "content-type": "application/json"
            }
        })
    }
}