import { weekStartSunday, toStringFromDate, toDateFromString } from "src/common/utils/date-utils";
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
import { expense_select, timesheet_select } from "src/time-and-attendance/utils/selects.utils";
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";
import { Injectable } from "@nestjs/common";
import { Result } from "src/common/errors/result-error.factory";
import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper";
import { ExpenseDto } from "src/time-and-attendance/expenses/expense-create.dto";
import { normalizeAndParseExpenseDto } from "src/time-and-attendance/expenses/expense.utils";
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";

@Injectable()
export class ExpenseUpdateService {
    constructor(
        private readonly prisma: PrismaPostgresService,
        private readonly emailResolver: EmailToIdResolver,
        private readonly typeResolver: BankCodesResolver,
        private readonly payPeriodEventService: PayPeriodEventService,
    ) { }
    //_________________________________________________________________
    //                              UPDATE
    //_________________________________________________________________
    async updateExpense(dto: ExpenseDto, email: string, employee_email?: string): Promise<Result<ExpenseDto, string>> {
        try {
            const account_email = employee_email ?? email;
            //fetch employee_id using req.user.email
            const employee_id = await this.emailResolver.findIdByEmail(account_email);
            if (!employee_id.success) return { success: false, error: employee_id.error };
            //normalize string , date format and parse numbers
            const normed_expense = await normalizeAndParseExpenseDto(dto);
            if (!normed_expense.success) return { success: false, error: normed_expense.error }

            const type = await this.typeResolver.findBankCodeIDByType(dto.type);
            if (!type.success) return { success: false, error: 'INVALID_EXPENSE_TYPE' }
            //added timesheet_id modification check according to the new date
            const new_timesheet_start_date = weekStartSunday(toDateFromString(dto.date));

            const timesheet = await this.prisma.timesheets.findFirst({
                where: { start_date: new_timesheet_start_date, employee_id: employee_id.data },
                select: timesheet_select,
            });
            if (!timesheet) return { success: false, error: `TIMESHEET_NOT_FOUND` }

            //checks for modifications
            const data = {
                ...normed_expense.data,
                bank_code_id: type.data,
                is_approved: dto.is_approved,
            };
            if (!data) return { success: false, error: `INVALID_EXPENSE` }

            //push updates and get updated datas
            const expense = await this.prisma.expenses.update({
                where: { id: dto.id, timesheet_id: timesheet.id },
                data,
                select: expense_select,
            });
            if (!expense) return { success: false, error: 'INVALID_EXPENSE' }

            //build an object to return to the frontend
            const updated: ExpenseDto = {
                ...expense,
                type: expense.bank_code.type,
                date: toStringFromDate(expense.date),
                amount: expense.amount?.toNumber(),
                mileage: expense.mileage?.toNumber(),
                attachment: expense.attachment ?? undefined,
                supervisor_comment: expense.supervisor_comment ?? undefined,
            };

            // notify timesheet-approval observers of changes, but only if it came
            // from timesheet and not timesheet-approval (no employee_email)
            if (!employee_email) {
                this.payPeriodEventService.emit({
                    employee_email: email,
                    event_type: 'expense',
                    action: 'update',
                });
            }

            return { success: true, data: updated };
        } catch (error) {
            return { success: false, error: 'EXPENSE_NOT_FOUND' };
        }
    }
}

