import { Injectable } from "@nestjs/common";
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";

import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
import { toDateFromString } from "src/common/utils/date-utils";
import { Result } from "src/common/errors/result-error.factory";

import { toCompanyCodeFromString } from "src/identity-and-account/employees/employee.utils";
import { EmployeeDetailedUpsertDto } from "src/identity-and-account/employees/employee-detailed.dto";
import { toBooleanFromString } from "src/identity-and-account/employees/services/employees-get.service";
import { PaidTimeOffDto } from "src/time-and-attendance/paid-time-off/paid-time-off.dto";


@Injectable()
export class EmployeesUpdateService {
    constructor(
        private readonly prisma: PrismaPostgresService,
        private readonly emailResolver: EmailToIdResolver,
    ) { }

    async updateEmployee(dto: EmployeeDetailedUpsertDto): Promise<Result<boolean, string>> {
        const user_id = await this.emailResolver.resolveUserIdWithEmail(dto.email);
        if (!user_id.success) return { success: false, error: 'EMPLOYEE_NOT_FOUND'}

        const employee = await this.prisma.employees.findFirst({
            where: { user_id: user_id.data },
            select: { id: true },
        })

        if (!employee) return { success: false, error: 'EMPLOYEE_NOT_FOUND'};

        const company_code = toCompanyCodeFromString(dto.company_name);
        const supervisor_id = await this.toIdFromFullName(dto.supervisor_full_name);
        const normalized_access = toBooleanFromString(dto.user_module_access);
        const last_work_day = dto.last_work_day ? toDateFromString(dto.last_work_day) : null;

        await this.prisma.$transaction(async (tx) => {
            await tx.users.update({
                where: { id: user_id.data },
                data: {
                    first_name: dto.first_name,
                    last_name: dto.last_name,
                    email: dto.email,
                    phone_number: dto.phone_number,
                    residence: dto.residence,
                },
            });

            await tx.userModuleAccess.upsert({
                where: { user_id: user_id.data },
                update: {
                    dashboard: normalized_access.dashboard,
                    employee_list: normalized_access.employee_list,
                    employee_management: normalized_access.employee_management,
                    personal_profile: normalized_access.personal_profile,
                    timesheets: normalized_access.timesheets,
                    timesheets_approval: normalized_access.timesheets_approval,
                    chatbot: normalized_access.chatbot,
                },
                create: {
                    user_id: user_id.data,
                    dashboard: normalized_access.dashboard,
                    employee_list: normalized_access.employee_list,
                    employee_management: normalized_access.employee_management,
                    personal_profile: normalized_access.personal_profile,
                    timesheets: normalized_access.timesheets,
                    timesheets_approval: normalized_access.timesheets_approval,
                    chatbot: normalized_access.chatbot,
                },
            });

            const employee_pto = dto.paid_time_off ?? new PaidTimeOffDto(employee.id);
            const last_updated = employee_pto.last_updated ? toDateFromString(employee_pto.last_updated) : null;

            await tx.paidTimeOff.upsert({
                where: { employee_id: employee_pto.employee_id },
                update: {
                    sick_hours: employee_pto.sick_hours,
                    vacation_hours: employee_pto.vacation_hours,
                    banked_hours: employee_pto.banked_hours,
                    last_updated: last_updated,
                },
                create: {
                    employee_id: employee_pto.employee_id,
                    sick_hours: employee_pto.sick_hours,
                    vacation_hours: employee_pto.vacation_hours,
                    banked_hours: employee_pto.banked_hours,
                    last_updated: last_updated,
                }
            })

            return tx.employees.update({
                where: { user_id: user_id.data },
                data: {
                    company_code: company_code,
                    job_title: dto.job_title,
                    daily_expected_hours: dto.daily_expected_hours,
                    first_work_day: toDateFromString(dto.first_work_day),
                    last_work_day: last_work_day,
                    is_supervisor: dto.is_supervisor,
                    supervisor_id: supervisor_id,
                    schedule_preset_id: dto.preset_id,
                    external_payroll_id: dto.external_payroll_id,
                },
            });

        });
        return { success: true, data: true };

    }

    private toIdFromFullName = async (full_name: string) => {
        const [first_name, last_name] = full_name.split(' ', 2);
        let supervisor = await this.prisma.users.findFirst({
            where: { first_name, last_name },
            select: { employee: { select: { id: true } } }
        });
        if (!supervisor) supervisor = null;
        return supervisor?.employee?.id;
    }
}