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

import { module_list, Modules } from "src/common/mappers/module-access.mapper";
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
import { toStringFromDate } from "src/common/utils/date-utils";
import { Result } from "src/common/errors/result-error.factory";

import { toStringFromCompanyCode } from "src/identity-and-account/employees/employee.utils";
import { EmployeeDetailedDto } from "src/identity-and-account/employees/employee-detailed.dto";

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

    async findListEmployees(): Promise<Result<Partial<EmployeeDetailedDto>[], string>> {
        const employee_list = await this.prisma.employees.findMany({
            select: {
                user: {
                    select: {
                        first_name: true,
                        last_name: true,
                        email: true,
                        phone_number: true,
                    },
                },
                supervisor: {
                    select: {
                        user: {
                            select: {
                                first_name: true,
                                last_name: true,
                            },
                        },
                    },
                },
                is_supervisor: true,
                daily_expected_hours: true,
                job_title: true,
                company_code: true,
                external_payroll_id: true,
                first_work_day: true,
                last_work_day: true,
                schedule_preset_id: true,

            }
        }).then(rows => rows.map(r => ({
            first_name: r.user.first_name,
            last_name: r.user.last_name,
            email: r.user.email,
            phone_number: r.user.phone_number,
            company_name: toStringFromCompanyCode(r.company_code),
            job_title: r.job_title ?? '',
            daily_expected_hours: r.daily_expected_hours,
            external_payroll_id: r.external_payroll_id,
            employee_full_name: `${r.user.first_name} ${r.user.last_name}`,
            is_supervisor: r.is_supervisor,
            supervisor_full_name: `${r.supervisor?.user.first_name} ${r.supervisor?.user.last_name}`,
            first_work_day: toStringFromDate(r.first_work_day),
            last_work_day: r.last_work_day ? toStringFromDate(r.last_work_day) : null,
            preset_id: r.schedule_preset_id ?? undefined,
        })));
        return { success: true, data: employee_list };
    };

    async findOwnProfile(email: string): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
        const user_id = await this.emailResolver.resolveUserIdWithEmail(email);
        if (!user_id.success) return { success: false, error: 'INVALID_USER' };

        const existing_profile = await this.prisma.employees.findUnique({
            where: { user_id: user_id.data },
            select: {
                user: {
                    select: {
                        first_name: true,
                        last_name: true,
                        email: true,
                        phone_number: true,
                        residence: true,
                    },
                },
                first_work_day: true,
                company_code: true,
                job_title: true,
                external_payroll_id: true,
                paid_time_off: true,
                is_supervisor: true,
                schedule_preset_id: true,
                daily_expected_hours: true,
                supervisor: {
                    select: {
                        id: true, user: {
                            select: {
                                first_name: true,
                                last_name: true,
                            },
                        },
                    },
                },
            },
        });
        if (!existing_profile) return { success: false, error: 'EMPLOYEE_NOT_FOUND' };

        const company_name = toStringFromCompanyCode(existing_profile.company_code);

        return {
            success: true, data: {
                first_name: existing_profile.user.first_name,
                last_name: existing_profile.user.last_name,
                email: existing_profile.user.email,
                supervisor_full_name: `${existing_profile.supervisor?.user.first_name} ${existing_profile.supervisor?.user.last_name}`,
                company_name: company_name,
                daily_expected_hours: existing_profile.daily_expected_hours,
                job_title: existing_profile.job_title ?? '',
                external_payroll_id: existing_profile.external_payroll_id,
                paid_time_off: {
                    vacation_hours: existing_profile.paid_time_off?.vacation_hours.toNumber() ?? 0,
                    banked_hours: existing_profile.paid_time_off?.banked_hours.toNumber() ?? 0,
                    last_updated: existing_profile.paid_time_off?.last_updated?.toISOString() ?? null,
                },
                is_supervisor: existing_profile.is_supervisor,
                phone_number: existing_profile.user.phone_number,
                residence: existing_profile.user.phone_number,
                first_work_day: toStringFromDate(existing_profile.first_work_day),
                preset_id: existing_profile.schedule_preset_id ?? undefined,
            },
        };
    };

    async findOneDetailedProfile(email: string, employee_email?: string): Promise<Result<EmployeeDetailedDto, string>> {
        const account_email = employee_email ?? email;
        const user_id = await this.emailResolver.resolveUserIdWithEmail(account_email);
        if (!user_id.success) return { success: false, error: 'INVALID_USER' };

        const employee = await this.prisma.employees.findUnique({
            where: { user_id: user_id.data },
            select: {
                user: {
                    select: {
                        first_name: true,
                        last_name: true,
                        email: true,
                        phone_number: true,
                        residence: true,
                        user_module_access: {
                            select: {
                                dashboard: true,
                                employee_list: true,
                                employee_management: true,
                                personal_profile: true,
                                timesheets: true,
                                timesheets_approval: true,
                                chatbot: true,
                            },
                        },
                    },
                },
                supervisor: {
                    select: {
                        user: {
                            select: {
                                first_name: true,
                                last_name: true,
                            },
                        },
                    },
                },
                job_title: true,
                company_code: true,
                first_work_day: true,
                last_work_day: true,
                external_payroll_id: true,
                paid_time_off: true,
                is_supervisor: true,
                daily_expected_hours: true,
                schedule_preset_id: true,
                schedule_preset: {
                    select: {
                        id: true,
                    }
                }
            }
        });
        if (!employee) return { success: false, error: `EMPLOYEE_NOT_FOUND` };
        if (!employee.user) return { success: false, error: 'USER_NOT_FOUND' };

        let module_access_array: Modules[] = [];
        if (employee.user.user_module_access) {
            module_access_array = toStringFromBoolean(employee.user.user_module_access);
        }

        const company_name = toStringFromCompanyCode(employee.company_code);

        return {
            success: true,
            data: {
                first_name: employee.user.first_name,
                last_name: employee.user.last_name,
                email: employee.user.email,
                residence: employee.user.residence ?? '',
                phone_number: employee.user.phone_number,
                company_name: company_name,
                is_supervisor: employee.is_supervisor ?? false,
                job_title: employee.job_title ?? '',
                external_payroll_id: employee.external_payroll_id,
                paid_time_off: {
                    id: employee.paid_time_off?.id ?? -1,
                    employee_id: employee.paid_time_off?.employee_id ?? -1,
                    sick_hours: employee.paid_time_off?.sick_hours.toNumber() ?? 0,
                    vacation_hours: employee.paid_time_off?.vacation_hours.toNumber() ?? 0,
                    banked_hours: employee.paid_time_off?.banked_hours.toNumber() ?? 0,
                    last_updated: employee.paid_time_off?.last_updated?.toISOString() ?? null,
                },
                employee_full_name: `${employee.user.first_name} ${employee.user.last_name}`,
                first_work_day: toStringFromDate(employee.first_work_day),
                last_work_day: employee.last_work_day ? toStringFromDate(employee.last_work_day) : undefined,
                supervisor_full_name: employee.supervisor ? `${employee.supervisor?.user.first_name} ${employee.supervisor?.user.last_name}` : '',
                user_module_access: module_access_array,
                daily_expected_hours: employee.daily_expected_hours,
                preset_id: employee.schedule_preset_id ? employee.schedule_preset_id : undefined,
            },
        };
    };
}

const createDefaultModuleAccess = (): Record<Modules, boolean> =>
    module_list.reduce((acc, mod) => {
        acc[mod] = false;
        return acc;
    }, {} as Record<Modules, boolean>);


export const toBooleanFromString = (arr?: readonly string[] | null): Record<Modules, boolean> => {
    const result = createDefaultModuleAccess();
    if (!arr || !Array.isArray(arr)) return result;
    for (const item of arr) {
        if (typeof item !== 'string') continue;
        const trimmed = item.trim();
        if ((module_list as readonly string[]).includes(trimmed)) {
            result[trimmed as Modules] = true;
        }
    }
    return result;
}

export const toStringFromBoolean = (boolean_module_access: Record<Modules, boolean>): Modules[] => {
    const access_array = Object.entries(boolean_module_access);
    const allowed_accesses = access_array.filter(([_key, value]) => value === true);
    return allowed_accesses.map(([key]) => key as Modules);
}