import prisma from "../config/prisma.js";
import { Prisma, PaymentMode } from "../generated/prisma/client.js";

export class ExpenseRepository {

    async create(data: Prisma.ExpenseCreateInput) {
        return prisma.expense.create({ data });
    }

    async findAll(skip?: number, take?: number, search?: string, startDate?: string, endDate?: string) {
        const where: Prisma.ExpenseWhereInput = {};

        if (search) {
            const amount = Number(search);

            where.OR = [
                { category: { contains: search } },
                { remarks: { contains: search } },
            ];

            if (!isNaN(amount) && search.trim() !== "") {
                where.OR.push({ amount: { equals: amount } });
            }

            const paymentMode = search.toUpperCase() as PaymentMode;
            if (Object.values(PaymentMode).includes(paymentMode)) {
                where.OR.push({ paymentMode: { equals: paymentMode } });
            }
        }

        if (startDate && endDate) {
            const start = new Date(startDate);
            start.setUTCHours(0, 0, 0, 0);

            const end = new Date(endDate);
            end.setUTCHours(23, 59, 59, 999);

            where.expenseDate = { gte: start, lte: end };
        } else if (startDate) {
            const start = new Date(startDate);
            start.setUTCHours(0, 0, 0, 0);

            const end = new Date(startDate);
            end.setUTCHours(23, 59, 59, 999);

            where.expenseDate = { gte: start, lte: end };
        }

        const [items, total, amountSum] = await Promise.all([
                   prisma.expense.findMany({
                   where,
                   ...(skip !== undefined ? { skip } : {}),
               ...(take !== undefined ? { take } : {}),
               orderBy: { id: 'desc' },
           }),
           prisma.expense.count({ where }),
           prisma.expense.aggregate({
               where,
               _sum: {
                   amount: true,
               },
           }),
       ]);

        if (amountSum) {
            return { items, total, amountSum: Number(amountSum._sum.amount ?? 0) };
        }
        return { items, total, amountSum: 0 };
    }

    async findById(id: number) {
        return prisma.expense.findUnique({ where: { id } });
    }

    async update(id: number, data: Prisma.ExpenseUpdateInput) {
        return prisma.expense.update({ where: { id }, data });
    }

    async delete(id: number) {
        return prisma.expense.delete({ where: { id } });
    }

    // Dashboard: sum of today's expenses
    async todayExpensesTotal(): Promise<number> {
        const today = new Date();
        const startOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0));
        const endOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999));

        const result = await prisma.expense.aggregate({
            _sum: { amount: true },
            where: { expenseDate: { gte: startOfDay, lte: endOfDay } },
        });
        return Number(result._sum.amount ?? 0);
    }

    //Export Excel code.

    async findAllForExport(search?: string, startDate?: string, endDate?: string) {
    const where: Prisma.ExpenseWhereInput = {};

    if (search) {
        const amount = Number(search);

        where.OR = [
            { category: { contains: search } },
            { remarks: { contains: search } },
        ];

        if (!isNaN(amount) && search.trim() !== "") {
            where.OR.push({ amount: { equals: amount } });
        }

        const paymentMode = search.toUpperCase() as PaymentMode;
        if (Object.values(PaymentMode).includes(paymentMode)) {
            where.OR.push({ paymentMode: { equals: paymentMode } });
        }
    }

    if (startDate && endDate) {
        const start = new Date(startDate);
        start.setUTCHours(0, 0, 0, 0);
        const end = new Date(endDate);
        end.setUTCHours(23, 59, 59, 999);
        where.expenseDate = { gte: start, lte: end };
    } else if (startDate) {
        const start = new Date(startDate);
        start.setUTCHours(0, 0, 0, 0);
        const end = new Date(startDate);
        end.setUTCHours(23, 59, 59, 999);
        where.expenseDate = { gte: start, lte: end };
    }

    return prisma.expense.findMany({
        where,
        orderBy: { id: 'desc' },
    });
    }

    async getMonthlyExpenses(year: number): Promise<{ month: string; amount: number }[]> {
        const startOfYear = new Date(year, 0, 1);
        const endOfYear = new Date(year, 11, 31, 23, 59, 59, 999);

        const expenses = await prisma.expense.findMany({
            where: {
                expenseDate: {
                    gte: startOfYear,
                    lte: endOfYear,
                },
            },
            select: {
                amount: true,
                expenseDate: true,
            },
        });

        const months = [
            'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
            'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
        ];
        const monthlySums = months.map(m => ({ month: m, amount: 0 }));

        expenses.forEach((item) => {
            if (item.expenseDate) {
                const date = new Date(item.expenseDate);
                const monthIdx = date.getMonth();
                if (monthIdx >= 0 && monthIdx < 12 && monthlySums[monthIdx]) {
                    monthlySums[monthIdx].amount += Number(item.amount) || 0;
                }
            }
        });

        return monthlySums;
    }
}