"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LeavesService = void 0;
const common_1 = require("@nestjs/common");
const date_fns_tz_1 = require("date-fns-tz");
const typeorm_1 = require("typeorm");
const permission_resolver_helpers_1 = require("../../common/access-control/permission-resolver.helpers");
const permission_resolver_service_1 = require("../../common/access-control/permission-resolver.service");
const org_timezone_service_1 = require("../../common/timezone/org-timezone.service");
const config_1 = require("../../config");
const entities_1 = require("../../config/database/entities");
const utils_1 = require("../../utils");
const day_off_attendance_1 = require("../../utils/day-off-attendance");
const notifications_service_1 = require("../notifications/notifications.service");
const leaves_dto_1 = require("./leaves.dto");
let LeavesService = class LeavesService {
    constructor(db, notificationService, permissionResolver, orgTimezone, uploadService) {
        this.db = db;
        this.notificationService = notificationService;
        this.permissionResolver = permissionResolver;
        this.orgTimezone = orgTimezone;
        this.uploadService = uploadService;
    }
    async getPermissions(ctx) {
        return this.permissionResolver.resolveForUser(ctx);
    }
    includeMember() {
        return {
            member: {
                include: {
                    department: true,
                },
            },
            approver_owner: true,
            approver_member: true,
        };
    }
    async moveLeaveDocument(document) {
        if (!document ||
            typeof document !== 'object' ||
            typeof document.file_name !== 'string' ||
            document.file_name.trim() === '') {
            return undefined;
        }
        const mimeType = String(document.mime_type ?? '').toLowerCase();
        if (!mimeType.startsWith('image/')) {
            throw (0, utils_1.createGeneralError)('Attachment must be an image', 'document', common_1.HttpStatus.BAD_REQUEST);
        }
        const moved = await this.uploadService.moveImage(document, 'leaves');
        return {
            file_name: moved.file_name,
            original_name: moved.original_name,
            file_size: moved.file_size,
            mime_type: moved.mime_type,
        };
    }
    async ensureMemberExists(memberId, owner_id) {
        const member = await this.db.members.findUnique({
            where: { id: memberId, owner_id },
            select: { id: true },
        });
        if (!member) {
            throw (0, utils_1.createGeneralError)('Member not found', 'member_id', common_1.HttpStatus.NOT_FOUND);
        }
    }
    async ensureNoOverlap(memberId, startDate, endDate, owner_id, excludeId) {
        const overlappingLeave = await this.db.leaves.findFirst({
            where: {
                owner_id,
                member_id: memberId,
                ...(excludeId ? { id: { not: excludeId } } : {}),
                status: {
                    in: ['pending', 'approved'],
                },
                start_date: {
                    lte: endDate,
                },
                end_date: {
                    gte: startDate,
                },
            },
            select: {
                id: true,
            },
        });
        if (overlappingLeave) {
            throw (0, utils_1.createGeneralError)('Leave overlaps with an existing approved or pending leave', 'date_range', common_1.HttpStatus.CONFLICT);
        }
    }
    async normalizeDayOffLeaveDates(owner_id, referenceDate) {
        const timeZone = await this.orgTimezone.getTimezone(owner_id);
        const dateKey = (0, date_fns_tz_1.formatInTimeZone)(referenceDate, timeZone, 'yyyy-MM-dd');
        const { startUtc, endUtc } = (0, utils_1.getUtcStartEndOfDate)(dateKey, timeZone);
        return {
            start_date: startUtc,
            end_date: endUtc,
        };
    }
    async validateDayOffLeave(member_id, owner_id, startDate, endDate) {
        await this.ensureMemberExists(member_id, owner_id);
        const timeZone = await this.orgTimezone.getTimezone(owner_id);
        const startKey = (0, date_fns_tz_1.formatInTimeZone)(startDate, timeZone, 'yyyy-MM-dd');
        const endKey = (0, date_fns_tz_1.formatInTimeZone)(endDate, timeZone, 'yyyy-MM-dd');
        if (startKey !== endKey) {
            throw (0, utils_1.createGeneralError)('Day off requests must be for a single day', 'end_date', common_1.HttpStatus.BAD_REQUEST);
        }
        const todayStr = (0, date_fns_tz_1.formatInTimeZone)(new Date(), timeZone, 'yyyy-MM-dd');
        const { startUtc: todayStart } = (0, utils_1.getUtcStartEndOfDate)(todayStr, timeZone);
        const { startUtc: requestStart } = (0, utils_1.getUtcStartEndOfDate)(startDate, timeZone);
        if (requestStart < todayStart) {
            throw (0, utils_1.createGeneralError)('Day off requests can only be made for today or future dates', 'start_date', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    isDayOffLeave(leaveType) {
        return leaveType === entities_1.LeaveTypeEnum.day_off;
    }
    async syncDayOffLeaveAttendance(leave, owner_id, previousStatus) {
        if (!this.isDayOffLeave(leave.leave_type)) {
            return;
        }
        const timeZone = await this.orgTimezone.getTimezone(owner_id);
        if (leave.status === 'approved') {
            await (0, day_off_attendance_1.applyDayOffAttendance)(this.db, {
                member_id: leave.member_id,
                owner_id,
                date: leave.start_date,
                reason: leave.reason,
                timeZone,
            });
            return;
        }
        if (previousStatus === 'approved' &&
            (leave.status === 'rejected' ||
                leave.status === 'cancelled' ||
                leave.status === 'pending')) {
            await (0, day_off_attendance_1.revertDayOffAttendance)(this.db, {
                member_id: leave.member_id,
                owner_id,
                date: leave.start_date,
                timeZone,
            });
        }
    }
    async applyLeaveAttendanceUpdate(leave, owner_id, status, previousStatus) {
        if (this.isDayOffLeave(leave.leave_type)) {
            await this.syncDayOffLeaveAttendance({ ...leave, status }, owner_id, previousStatus);
            return;
        }
        if (status === 'approved') {
            void this.updateAttendanceForLeave(leave.start_date, leave.end_date, leave.member_id, owner_id, entities_1.AttendanceStatusEnum.on_leave);
            return;
        }
        if (status === 'rejected' ||
            status === 'cancelled' ||
            (status === 'pending' && previousStatus === 'approved')) {
            void this.updateAttendanceForLeave(leave.start_date, leave.end_date, leave.member_id, owner_id, entities_1.AttendanceStatusEnum.absent);
        }
    }
    async createLeave(data, owner_id, ctx) {
        try {
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'leave.access');
            (0, permission_resolver_helpers_1.assertScopedAction)(permissions, 'leave.create', data.member_id, ctx.sub);
            let startDate = data.start_date;
            let endDate = data.end_date;
            if (this.isDayOffLeave(data.leave_type)) {
                const normalized = await this.normalizeDayOffLeaveDates(owner_id, startDate);
                startDate = normalized.start_date;
                endDate = normalized.end_date;
            }
            if (endDate < startDate) {
                throw (0, utils_1.createGeneralError)('End date must be after or equal to start date', 'end_date', common_1.HttpStatus.BAD_REQUEST);
            }
            await this.ensureNoOverlap(data.member_id, startDate, endDate, owner_id);
            if (this.isDayOffLeave(data.leave_type)) {
                await this.validateDayOffLeave(data.member_id, owner_id, startDate, endDate);
            }
            const document = await this.moveLeaveDocument(data.document);
            const leave = await this.db.leaves.create({
                data: {
                    member_id: data.member_id,
                    start_date: startDate,
                    end_date: endDate,
                    owner_id,
                    ...(data.reason ? { reason: data.reason } : {}),
                    ...(data.status ? { status: data.status } : {}),
                    ...(document ? { document } : {}),
                    leave_type: data.leave_type ?? 'annual',
                    ...(data?.status !== 'pending'
                        ? data?.approver_type === 'member'
                            ? {
                                approver_type: 'member',
                                approver_member_id: data.approver_id,
                                approver_owner_id: null,
                            }
                            : {
                                approver_type: data.approver_type,
                                approver_owner_id: data.approver_id,
                                approver_member_id: null,
                            }
                        : {
                            approver_type: null,
                            approver_member_id: null,
                            approver_owner_id: null,
                        }),
                },
                include: this.includeMember(),
            });
            const { data: parsedLeave } = leaves_dto_1.leaveResponseSchema.safeParse(leave);
            this.notificationService.sendNotification({
                owner_id,
                title: 'New Leave Request',
                message: `${leave.member.first_name} has submitted a leave request from ${(0, utils_1.getUserTimzoneDate)(leave?.start_date)} to ${(0, utils_1.getUserTimzoneDate)(leave?.end_date)}. Please review and take the necessary action.`,
                type: entities_1.NotificationTypeEnum.leave,
                reference_id: leave.id,
                start_time: leave.start_date,
                end_time: leave.end_date,
                meta: {
                    leave_type: leave.leave_type,
                },
                member_id: data?.status !== 'pending' ? data.member_id : undefined,
                include_owner: data?.status === 'pending' ? true : false,
                include_admins: data?.status === 'pending' ? true : false,
            });
            if (data?.status !== 'pending') {
                await this.applyLeaveAttendanceUpdate(leave, owner_id, data.status ?? 'pending');
            }
            return {
                data: parsedLeave,
                message: 'Leave created successfully',
                status: common_1.HttpStatus.CREATED,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in createLeave: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async getLeaves(query, owner_id, ctx) {
        try {
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'leave.access');
            const scopeFilter = (0, permission_resolver_helpers_1.resolveDataScope)(permissions, 'leave.view', query.data_scope, ctx);
            const memberFilter = (0, permission_resolver_helpers_1.buildMemberFilter)(scopeFilter, ctx.sub);
            const page = query.page || 1;
            const limit = query.limit || 10;
            const skip = (page - 1) * limit;
            const asnDate = query?.start_date && query?.end_date
                ? (0, typeorm_1.Between)(query.start_date, query.end_date)
                : undefined;
            if (query.member_id) {
                (0, permission_resolver_helpers_1.assertScopedAction)(permissions, 'leave.view', query.member_id, ctx.sub);
            }
            const where = {
                owner_id,
                ...memberFilter,
                ...(query.member_id && { member_id: query.member_id }),
                ...(query.status && { status: query.status }),
                ...(asnDate && { start_date: asnDate }),
                ...(query.department_id && {
                    member: { department_id: query.department_id },
                }),
            };
            const [leaves, total] = await Promise.all([
                this.db.leaves.findMany({
                    where,
                    include: this.includeMember(),
                    orderBy: { start_date: 'desc' },
                    skip,
                    take: limit,
                }),
                this.db.leaves.count({ where }),
            ]);
            const { data: parsedLeaves, error } = leaves_dto_1.leavesResponseSchema.safeParse(leaves);
            const summary = {
                total_approved: 0,
                total_pending: 0,
                total_rejected: 0,
            };
            for (const item of leaves) {
                if (item.status === 'approved')
                    summary.total_approved++;
                else if (item.status === 'pending')
                    summary.total_pending++;
                else if (item.status === 'rejected')
                    summary.total_rejected++;
            }
            const totalPages = Math.ceil(total / limit);
            return {
                data: parsedLeaves,
                message: 'Leaves retrieved successfully',
                status: common_1.HttpStatus.OK,
                details: {
                    pagination: {
                        total_items: total,
                        total_pages: totalPages,
                        current_page: page,
                        items_per_page: limit,
                        has_next_page: page < totalPages,
                        has_previous_page: page > 1,
                        next_page: page < totalPages ? page + 1 : null,
                        previous_page: page > 1 ? page - 1 : null,
                    },
                    summary: {
                        ...summary,
                        total_requests: total,
                    },
                },
            };
        }
        catch (error) {
            config_1.logger.error(`Error in getLeaves: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse)
                throw error;
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async getLeaveById(id, owner_id, ctx) {
        try {
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'leave.access');
            const leave = await this.db.leaves.findUnique({
                where: { id, owner_id },
                include: this.includeMember(),
            });
            if (!leave) {
                throw (0, utils_1.createGeneralError)('Leave not found', 'leave', common_1.HttpStatus.NOT_FOUND);
            }
            (0, permission_resolver_helpers_1.assertScopedAction)(permissions, 'leave.view', leave.member_id, ctx.sub);
            const { data: parsedLeave } = leaves_dto_1.leaveResponseSchema.safeParse(leave);
            return {
                data: parsedLeave,
                message: 'Leave retrieved successfully',
                status: common_1.HttpStatus.OK,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in getLeaveById: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async updateLeave(id, data, owner_id, ctx) {
        try {
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'leave.access');
            const existingLeave = await this.db.leaves.findUnique({
                where: { id, owner_id },
            });
            if (!existingLeave) {
                throw (0, utils_1.createGeneralError)('Leave not found', 'leave', common_1.HttpStatus.NOT_FOUND);
            }
            (0, permission_resolver_helpers_1.assertScopedAction)(permissions, 'leave.update', existingLeave.member_id, ctx.sub);
            const nextMemberId = data.member_id ?? existingLeave.member_id;
            let nextStartDate = data.start_date ?? existingLeave.start_date;
            let nextEndDate = data.end_date ?? existingLeave.end_date;
            const previousStatus = existingLeave.status;
            const nextStatus = data.status ?? existingLeave.status;
            const statusChanged = previousStatus !== nextStatus;
            const isFinalStatus = nextStatus !== 'pending';
            const nextLeaveType = data.leave_type ?? existingLeave.leave_type;
            if (this.isDayOffLeave(nextLeaveType)) {
                const normalized = await this.normalizeDayOffLeaveDates(owner_id, nextStartDate);
                nextStartDate = normalized.start_date;
                nextEndDate = normalized.end_date;
            }
            if (nextEndDate < nextStartDate) {
                throw (0, utils_1.createGeneralError)('End date must be after or equal to start date', 'end_date', common_1.HttpStatus.BAD_REQUEST);
            }
            await this.ensureMemberExists(nextMemberId, owner_id);
            await this.ensureNoOverlap(nextMemberId, nextStartDate, nextEndDate, owner_id, id);
            if (this.isDayOffLeave(nextLeaveType)) {
                await this.validateDayOffLeave(nextMemberId, owner_id, nextStartDate, nextEndDate);
            }
            const nextDocument = data.document === null
                ? null
                : data.document
                    ? await this.moveLeaveDocument(data.document)
                    : undefined;
            const updatedLeave = await this.db.leaves.update({
                where: { id },
                data: {
                    ...(data.member_id
                        ? {
                            member_id: data.member_id,
                        }
                        : {}),
                    ...(data.start_date || this.isDayOffLeave(nextLeaveType)
                        ? { start_date: nextStartDate }
                        : {}),
                    ...(data.end_date || this.isDayOffLeave(nextLeaveType)
                        ? { end_date: nextEndDate }
                        : {}),
                    ...(data.reason !== undefined ? { reason: data.reason } : {}),
                    ...(data.status ? { status: data.status } : {}),
                    ...(nextDocument !== undefined ? { document: nextDocument } : {}),
                    ...(data?.status !== 'pending'
                        ? data?.approver_type === 'member'
                            ? {
                                approver_type: 'member',
                                approver_member_id: data.approver_id,
                                approver_owner_id: null,
                            }
                            : {
                                approver_type: data.approver_type,
                                approver_owner_id: data.approver_id,
                                approver_member_id: null,
                            }
                        : {
                            approver_type: null,
                            approver_member_id: data.approver_id,
                            approver_owner_id: null,
                        }),
                },
                include: this.includeMember(),
            });
            const { data: parsedLeave } = leaves_dto_1.leaveResponseSchema.safeParse(updatedLeave);
            if (statusChanged && isFinalStatus) {
                this.notificationService.sendNotification({
                    owner_id,
                    member_id: updatedLeave.member_id,
                    type: entities_1.NotificationTypeEnum.leave,
                    title: nextStatus === 'approved' ? 'Leave Approved' : 'Leave Rejected',
                    message: `Your leave request for ${(0, utils_1.getUserTimzoneDate)(updatedLeave?.start_date)} to ${(0, utils_1.getUserTimzoneDate)(updatedLeave.end_date)} has been ${nextStatus}. Please check the latest status and updates.`,
                    reference_id: updatedLeave.id,
                    start_time: updatedLeave.start_date,
                    end_time: updatedLeave.end_date,
                    meta: {
                        leave_type: updatedLeave.leave_type,
                        reason: updatedLeave.reason,
                    },
                });
            }
            if (statusChanged) {
                await this.applyLeaveAttendanceUpdate({
                    member_id: nextMemberId,
                    start_date: nextStartDate,
                    end_date: nextEndDate,
                    reason: updatedLeave.reason,
                    leave_type: updatedLeave.leave_type,
                }, owner_id, nextStatus, previousStatus);
            }
            return {
                data: parsedLeave,
                message: 'Leave updated successfully',
                status: common_1.HttpStatus.OK,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in updateLeave: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async updateBulkLeaveStatus(data, owner_id, ctx) {
        try {
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'leave.access');
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'leave.approve');
            const leaves = await this.db.leaves.findMany({
                where: {
                    id: {
                        in: data.leave_ids,
                    },
                },
                include: {
                    member: true,
                },
            });
            if (!leaves.length) {
                throw (0, utils_1.createGeneralError)('Leaves not found', 'leave_ids', common_1.HttpStatus.NOT_FOUND);
            }
            for (const leave of leaves) {
                (0, permission_resolver_helpers_1.assertScopedAction)(permissions, 'leave.view', leave.member_id, ctx.sub);
            }
            const updateData = {
                status: data?.status,
                approver_type: data?.approver_type ?? null,
                approver_owner_id: data?.approver_type === 'owner' ? data.approver_id : null,
                approver_member_id: data?.approver_type === 'member' ? data.approver_id : null,
                approved_at: new Date(),
            };
            const updatedLeaves = await this.db.leaves.updateMany({
                where: {
                    id: { in: data.leave_ids },
                },
                data: updateData,
            });
            this.notificationService.sendNotification({
                owner_id: owner_id,
                member_ids: leaves.map((leave) => leave.member_id),
                type: entities_1.NotificationTypeEnum.leave,
                title: data.status === 'approved'
                    ? 'Leave Approved'
                    : data.status === 'rejected'
                        ? 'Leave Rejected'
                        : 'Leave Status Updated',
                message: `Your leave request status has been updated to ${data.status}. Please review the updated leave information and contact your administrator if you need further clarification.`,
                reference_id: null,
                include_owner: false,
                include_admins: false,
            });
            for (const leave of leaves) {
                await this.applyLeaveAttendanceUpdate({
                    member_id: leave.member_id,
                    start_date: leave.start_date,
                    end_date: leave.end_date,
                    reason: leave.reason,
                    leave_type: leave.leave_type,
                }, owner_id, data.status);
            }
            return {
                data: updatedLeaves,
                message: 'Leave statuses updated successfully',
                status: common_1.HttpStatus.OK,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in updateBulkLeaveStatus: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async updateLeaveStatus(id, data, owner_id) {
        try {
            const existingLeave = await this.db.leaves.findUnique({
                where: { id, owner_id },
            });
            if (!existingLeave) {
                throw (0, utils_1.createGeneralError)('Leave not found', 'leave', common_1.HttpStatus.NOT_FOUND);
            }
            const { status, approver_id, approver_type } = data;
            if (status === 'approved' || status === 'rejected') {
                if (!approver_type) {
                    throw (0, utils_1.createGeneralError)('Approver type is required when approving or rejecting leave', 'approver_type', common_1.HttpStatus.BAD_REQUEST);
                }
                if (!approver_id) {
                    throw (0, utils_1.createGeneralError)('Approver ID is required when approving or rejecting leave', 'approver_id', common_1.HttpStatus.BAD_REQUEST);
                }
            }
            const updateData = {
                status,
            };
            if (status === 'approved' || status === 'rejected') {
                if (approver_type === 'owner') {
                    if (approver_id !== owner_id) {
                        throw (0, utils_1.createGeneralError)('Owner approver ID must match authenticated owner', 'approver_id', common_1.HttpStatus.BAD_REQUEST);
                    }
                    const owner = await this.db.owner.findUnique({
                        where: { id: approver_id },
                        select: { id: true },
                    });
                    if (!owner) {
                        throw (0, utils_1.createGeneralError)('Owner approver not found', 'approver_id', common_1.HttpStatus.NOT_FOUND);
                    }
                    updateData.approver_type = 'owner';
                    updateData.approver_owner_id = approver_id;
                    updateData.approver_member_id = null;
                }
                if (approver_type === 'member') {
                    const member = await this.db.members.findUnique({
                        where: {
                            id: approver_id,
                            owner_id,
                        },
                        select: { id: true },
                    });
                    if (!member) {
                        throw (0, utils_1.createGeneralError)('Member approver not found', 'approver_id', common_1.HttpStatus.NOT_FOUND);
                    }
                    updateData.approver_type = 'member';
                    updateData.approver_member_id = approver_id;
                    updateData.approver_owner_id = null;
                }
                updateData.approved_at = status === 'approved' ? new Date() : null;
                this.notificationService.sendNotification({
                    owner_id,
                    member_id: existingLeave.member_id,
                    type: entities_1.NotificationTypeEnum.leave,
                    title: status === 'approved' ? 'Leave Approved' : 'Leave Rejected',
                    message: `Your leave request for ${(0, utils_1.getUserTimzoneDate)(existingLeave.start_date)} to ${(0, utils_1.getUserTimzoneDate)(existingLeave.end_date)} has been ${status}. Please check the latest update in your leave history.`,
                    reference_id: existingLeave.id,
                    start_time: existingLeave.start_date,
                    end_time: existingLeave.end_date,
                    meta: {
                        leave_type: existingLeave.leave_type,
                        reason: existingLeave.reason,
                    },
                });
                await this.applyLeaveAttendanceUpdate({
                    member_id: existingLeave.member_id,
                    start_date: existingLeave.start_date,
                    end_date: existingLeave.end_date,
                    reason: existingLeave.reason,
                    leave_type: existingLeave.leave_type,
                }, owner_id, status, existingLeave.status);
            }
            else {
                updateData.approver_type = null;
                updateData.approver_owner_id = null;
                updateData.approver_member_id = null;
                updateData.approved_at = null;
            }
            const leave = await this.db.leaves.update({
                where: { id },
                data: updateData,
                include: this.includeMember(),
            });
            const { data: parsedLeave } = leaves_dto_1.leaveResponseSchema.safeParse(leave);
            return {
                data: parsedLeave,
                message: 'Leave status updated successfully',
                status: common_1.HttpStatus.OK,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in updateLeaveStatus: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async deleteLeave(id, owner_id, ctx) {
        try {
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'leave.access');
            const existingLeave = await this.db.leaves.findUnique({
                where: { id, owner_id },
            });
            if (!existingLeave) {
                throw (0, utils_1.createGeneralError)('Leave not found', 'leave', common_1.HttpStatus.NOT_FOUND);
            }
            (0, permission_resolver_helpers_1.assertScopedAction)(permissions, 'leave.delete', existingLeave.member_id, ctx.sub);
            await this.db.leaves.delete({
                where: { id },
            });
            if (existingLeave.status === 'approved') {
                if (this.isDayOffLeave(existingLeave.leave_type)) {
                    const timeZone = await this.orgTimezone.getTimezone(owner_id);
                    await (0, day_off_attendance_1.revertDayOffAttendance)(this.db, {
                        member_id: existingLeave.member_id,
                        owner_id,
                        date: existingLeave.start_date,
                        timeZone,
                    });
                }
                else {
                    void this.updateAttendanceForLeave(existingLeave.start_date, existingLeave.end_date, existingLeave.member_id, owner_id, entities_1.AttendanceStatusEnum.absent);
                }
            }
            return {
                data: null,
                message: 'Leave deleted successfully',
                status: common_1.HttpStatus.OK,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in deleteLeave: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async getMembersLeaveBalance(owner_id, ctx, dataScope) {
        try {
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'leave.access');
            const scopeFilter = (0, permission_resolver_helpers_1.resolveDataScope)(permissions, 'leave.view', dataScope, ctx);
            const memberFilter = (0, permission_resolver_helpers_1.buildMemberFilter)(scopeFilter, ctx.sub);
            const members = await this.db.members.findMany({
                where: { owner_id, ...memberFilter },
                include: {
                    department: true,
                },
            });
            if (members.length === 0) {
                return {
                    data: [],
                    message: 'No members found',
                    status: common_1.HttpStatus.OK,
                    details: null,
                };
            }
            const policy = await this.db.leave_policy.findUnique({
                where: { owner_id },
            });
            const totalLeaves = Object.entries(policy)
                .filter(([key]) => key.endsWith('_leave_days') && key !== 'annual_leave_days')
                .reduce((sum, [, value]) => sum + Number(value), 0);
            const currentYear = new Date().getFullYear();
            const leaves = await this.db.leaves.findMany({
                where: {
                    owner_id,
                    member_id: { in: members.map((m) => m.id) },
                    start_date: {
                        gte: new Date(currentYear, 0, 1),
                        lte: new Date(currentYear, 11, 31),
                    },
                    status: 'approved',
                },
            });
            const leaveBalance = members.map((member) => {
                const memberLeaves = leaves.filter((leave) => leave.member_id === member.id);
                const usedLeaves = memberLeaves.reduce((sum, leave) => {
                    if (leave.leave_type === entities_1.LeaveTypeEnum.day_off) {
                        return sum;
                    }
                    const start = new Date(leave.start_date);
                    const end = new Date(leave.end_date);
                    const msPerDay = 1000 * 60 * 60 * 24;
                    if (end.getTime() < start.getTime())
                        return sum;
                    const days = Math.floor((end.getTime() - start.getTime()) / msPerDay) + 1;
                    return sum + days;
                }, 0);
                return {
                    id: member.id,
                    member_id: member.id,
                    first_name: member?.first_name,
                    last_name: member?.last_name,
                    image: member?.image,
                    department_id: member?.department_id,
                    department_name: member.department.name,
                    used_leaves: usedLeaves,
                    remaining_leaves: Math.max(totalLeaves - usedLeaves, 0),
                    total_leaves: totalLeaves,
                };
            });
            const { data: parsedLeaveBalance } = leaves_dto_1.leaveBalanceResponseArraySchema.safeParse(leaveBalance);
            return {
                data: parsedLeaveBalance,
                message: 'Leave balance retrieved successfully',
                status: common_1.HttpStatus.OK,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in getMembersLeaveBalance: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async updateAttendanceForLeave(startDate, endDate, member_id, owner_id, status = entities_1.AttendanceStatusEnum.on_leave) {
        try {
            console.log('Updating attendance for leave before date check', {
                member_id,
                owner_id,
                startDate,
                endDate,
            });
            const timeZone = await this.orgTimezone.getTimezone(owner_id);
            const { endUtc } = (0, utils_1.getUtcStartEndOfDate)(new Date(), timeZone);
            const effectiveEndDate = endDate > endUtc ? endUtc : endDate;
            if (startDate > endUtc) {
                return;
            }
            console.log('Updating attendance for leave after date check', {
                member_id,
                owner_id,
                startDate,
                endDate,
                effectiveEndDate,
            });
            const res = await this.db.attendances.updateMany({
                where: {
                    member_id,
                    owner_id,
                    date: (0, typeorm_1.Between)(startDate, effectiveEndDate),
                    check_in: (0, typeorm_1.IsNull)(),
                },
                data: {
                    status: status,
                },
            });
            console.log('Attendance updated for leave', {
                member_id,
                owner_id,
                startDate,
                effectiveEndDate,
                res,
            });
        }
        catch (err) {
            config_1.logger.error('Failed to update attendance for leave', err);
        }
    }
};
exports.LeavesService = LeavesService;
exports.LeavesService = LeavesService = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [config_1.DatabaseService,
        notifications_service_1.NotificationsService,
        permission_resolver_service_1.PermissionResolverService,
        org_timezone_service_1.OrgTimezoneService,
        config_1.UploadService])
], LeavesService);
//# sourceMappingURL=leaves.service.js.map