"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.HolidaysService = void 0;
const common_1 = require("@nestjs/common");
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 holiday_1 = require("../../utils/holiday");
const notifications_service_1 = require("../notifications/notifications.service");
const holidays_dto_1 = require("./holidays.dto");
let HolidaysService = class HolidaysService {
    constructor(db, notificationService, permissionResolver, orgTimezone) {
        this.db = db;
        this.notificationService = notificationService;
        this.permissionResolver = permissionResolver;
        this.orgTimezone = orgTimezone;
    }
    async getPermissions(ctx) {
        return this.permissionResolver.resolveForUser(ctx);
    }
    async createHoliday(data, owner_id, ctx) {
        try {
            const timeZone = await this.orgTimezone.getTimezone(owner_id);
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'holidays.access');
            (0, permission_resolver_helpers_1.assertScopedAction)(permissions, 'holidays.create', ctx.sub, ctx.sub);
            const { startUtc } = (0, utils_1.getUtcStartEndOfDate)(data.start_date, timeZone);
            const { endUtc } = (0, utils_1.getUtcStartEndOfDate)(data.end_date, timeZone);
            const startDate = data?.start_date;
            const endDate = data?.end_date;
            const departmentIds = data.department_ids ?? [];
            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 (0, holiday_1.ensureHolidayDepartmentIdsExist)(this.db, departmentIds, owner_id);
            const existingHoliday = await this.db.holidays.findFirst({
                where: {
                    owner_id,
                    name: data.name,
                    start_date: startUtc,
                    end_date: endUtc,
                    is_recurring: data.is_recurring ?? false,
                },
                select: {
                    id: true,
                },
            });
            if (existingHoliday) {
                throw (0, utils_1.createGeneralError)('Holiday with the same name and date range already exists', 'name', common_1.HttpStatus.CONFLICT);
            }
            const holiday = await this.db.holidays.create({
                data: {
                    name: data.name,
                    holiday_type: data.holiday_type ?? 'public',
                    start_date: startDate,
                    end_date: endDate,
                    is_recurring: data.is_recurring ?? false,
                    ...(data.description ? { description: data.description } : {}),
                    ...(departmentIds.length > 0
                        ? {
                            departments: {
                                connect: departmentIds.map((id) => ({ id })),
                            },
                        }
                        : {}),
                    owner_id,
                },
                include: (0, holiday_1.includeHolidayDepartments)(),
            });
            this.notificationService.sendNotification({
                owner_id,
                title: data?.name,
                start_time: data?.start_date,
                end_time: data?.end_date,
                message: `A new holiday has been scheduled from ${(0, utils_1.getUserTimzoneDate)(data.start_date, timeZone)} to ${(0, utils_1.getUserTimzoneDate)(data.end_date, timeZone)}. Please review the holiday schedule and plan your work activities accordingly.`,
                type: entities_1.NotificationTypeEnum.holiday,
                reference_id: holiday?.id,
                include_owner: true,
                include_admins: true,
                department_ids: departmentIds,
                meta: {
                    holiday_id: holiday?.id,
                    holiday_type: data?.holiday_type,
                },
            });
            const { data: parsedHoliday } = holidays_dto_1.holidayResponseSchema.safeParse(holiday);
            return {
                data: parsedHoliday,
                message: 'Holiday created successfully',
                status: common_1.HttpStatus.CREATED,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in createHoliday: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async getHolidays(query, owner_id, ctx) {
        try {
            const timeZone = await this.orgTimezone.getTimezone(owner_id);
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'holidays.access');
            const scopeFilter = (0, permission_resolver_helpers_1.resolveDataScope)(permissions, 'holidays.view', query.data_scope, ctx);
            let effectiveQuery = query;
            if (scopeFilter === 'own') {
                const member = await this.db.members.findUnique({
                    where: { id: ctx.sub, owner_id },
                    select: { department_id: true },
                });
                if (!member?.department_id) {
                    const page = query.page ?? 1;
                    const limit = query.limit ?? 10;
                    return {
                        data: [],
                        message: 'Holidays retrieved successfully',
                        status: common_1.HttpStatus.OK,
                        details: {
                            summary: {
                                total_holidays: 0,
                                total_recurring: 0,
                                total_public: 0,
                                total_company: 0,
                            },
                            pagination: {
                                total_items: 0,
                                total_pages: 0,
                                current_page: page,
                                items_per_page: limit,
                                has_next_page: false,
                                has_previous_page: false,
                                next_page: null,
                                previous_page: null,
                            },
                        },
                    };
                }
                effectiveQuery = {
                    ...query,
                    department_id: member.department_id,
                };
            }
            const { startUtc, endUtc } = (0, holiday_1.getHolidayUtcMonthStartEndFromDate)(undefined, timeZone);
            const from = effectiveQuery.start_date
                ? (0, utils_1.getUtcStartEndOfDate)(effectiveQuery.start_date, timeZone).startUtc
                : startUtc;
            const to = effectiveQuery.end_date
                ? (0, utils_1.getUtcStartEndOfDate)(effectiveQuery.end_date, timeZone).endUtc
                : endUtc;
            if (from > to) {
                throw (0, utils_1.createGeneralError)('Invalid date range. start date must be before end date', 'start_date', common_1.HttpStatus.BAD_REQUEST);
            }
            console.log(startUtc, endUtc, from, to);
            const page = effectiveQuery.page ?? 1;
            const limit = effectiveQuery.limit ?? 10;
            const where = { owner_id, ...(0, holiday_1.buildHolidayWhereFilter)(effectiveQuery) };
            const [nonRecurring, recurring] = await Promise.all([
                this.db.holidays.findMany({
                    where: {
                        ...where,
                        is_recurring: false,
                        start_date: {
                            lte: to,
                        },
                        end_date: {
                            gte: from,
                        },
                    },
                    include: (0, holiday_1.includeHolidayDepartments)(),
                }),
                this.db.holidays.findMany({
                    where: {
                        ...where,
                        is_recurring: true,
                    },
                    include: (0, holiday_1.includeHolidayDepartments)(),
                }),
            ]);
            const filterByDepartment = (holidays) => {
                if (!effectiveQuery.department_id) {
                    return holidays;
                }
                return holidays.filter((holiday) => holiday?.departments.some((dept) => dept.id === effectiveQuery.department_id));
            };
            const filteredNonRecurring = filterByDepartment(nonRecurring);
            const filteredRecurring = filterByDepartment(recurring);
            const recurringExpanded = filteredRecurring.flatMap((holiday) => {
                const baseStartDate = holiday?.start_date;
                const baseEndDate = holiday?.end_date;
                const startYear = from.getUTCFullYear();
                const endYear = to.getUTCFullYear();
                const rows = [];
                for (let year = startYear; year <= endYear; year += 1) {
                    const occurrence = (0, holiday_1.buildHolidayRecurringRange)(baseStartDate, baseEndDate, year, timeZone);
                    if (!occurrence) {
                        continue;
                    }
                    if (occurrence.occurrenceEnd < from ||
                        occurrence.occurrenceStart > to) {
                        continue;
                    }
                    rows.push({
                        ...holiday,
                        start_date: occurrence.occurrenceStart,
                        end_date: occurrence.occurrenceEnd,
                        occurrence_date: occurrence.occurrenceStart.toISOString(),
                        generated_from_recurring: true,
                    });
                }
                return rows;
            });
            const fixedRows = filteredNonRecurring.map((holiday) => ({
                ...holiday,
                start_date: holiday?.start_date,
                end_date: holiday?.end_date,
                occurrence_date: holiday?.start_date?.toISOString(),
                generated_from_recurring: false,
            }));
            const merged = [...fixedRows, ...recurringExpanded]
                .sort((a, b) => {
                const aTime = new Date(a.start_date).getTime();
                const bTime = new Date(b.start_date).getTime();
                return aTime - bTime;
            })
                .map((holiday) => ({
                ...holiday,
                start_date: holiday?.start_date,
                end_date: holiday?.end_date,
            }));
            const total = merged.length;
            const totalPages = Math.ceil(total / limit);
            const start = (page - 1) * limit;
            const paginated = merged.slice(start, start + limit);
            const summary = (0, holiday_1.buildHolidaySummary)(merged.map((holiday) => ({
                start_date: holiday?.start_date,
                end_date: holiday?.end_date,
                is_recurring: holiday?.is_recurring,
                holiday_type: holiday?.holiday_type,
            })));
            const { data: parsedHolidays } = holidays_dto_1.holidaysResponseSchema.safeParse(paginated);
            return {
                data: parsedHolidays,
                message: 'Holidays retrieved successfully',
                status: common_1.HttpStatus.OK,
                details: {
                    summary,
                    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,
                    },
                },
            };
        }
        catch (error) {
            config_1.logger.error(`Error in getHolidays: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async getHolidayById(id, owner_id, ctx) {
        try {
            const timeZone = await this.orgTimezone.getTimezone(owner_id);
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'holidays.access');
            (0, permission_resolver_helpers_1.assertScope)(permissions, 'holidays.view');
            const holiday = await this.db.holidays.findUnique({
                where: { id, owner_id },
                include: (0, holiday_1.includeHolidayDepartments)(),
            });
            if (!holiday) {
                throw (0, utils_1.createGeneralError)('Holiday not found', 'holiday', common_1.HttpStatus.NOT_FOUND);
            }
            const { data: parsedHoliday } = holidays_dto_1.holidayResponseSchema.safeParse({
                ...holiday,
                occurrence_date: holiday?.start_date.toISOString(),
                generated_from_recurring: false,
            });
            return {
                data: parsedHoliday,
                message: 'Holiday retrieved successfully',
                status: common_1.HttpStatus.OK,
                details: holiday?.is_recurring
                    ? {
                        upcoming_occurrences: (0, holiday_1.buildUpcomingHolidayRecurringDates)(holiday?.start_date, 5, timeZone),
                    }
                    : null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in getHolidayById: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async updateHoliday(id, data, owner_id, ctx) {
        try {
            const timeZone = await this.orgTimezone.getTimezone(owner_id);
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'holidays.access');
            (0, permission_resolver_helpers_1.assertScope)(permissions, 'holidays.update');
            const existingHoliday = await this.db.holidays.findUnique({
                where: { id, owner_id },
                include: (0, holiday_1.includeHolidayDepartments)(),
            });
            if (!existingHoliday) {
                throw (0, utils_1.createGeneralError)('Holiday not found', 'holiday', common_1.HttpStatus.NOT_FOUND);
            }
            const nextName = data.name ?? existingHoliday.name;
            const nextStartDate = data?.start_date
                ? data.start_date
                : existingHoliday.start_date;
            const nextEndDate = data?.end_date
                ? data.end_date
                : existingHoliday.end_date;
            const { startUtc } = (0, utils_1.getUtcStartEndOfDate)(nextStartDate, timeZone);
            const { endUtc } = (0, utils_1.getUtcStartEndOfDate)(nextEndDate, timeZone);
            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);
            }
            const conflictingHoliday = await this.db.holidays.findFirst({
                where: {
                    owner_id,
                    name: nextName,
                    start_date: startUtc,
                    end_date: endUtc,
                    id: {
                        not: id,
                    },
                },
                select: {
                    id: true,
                },
            });
            if (conflictingHoliday) {
                throw (0, utils_1.createGeneralError)('Holiday with the same name and date range already exists', 'name', common_1.HttpStatus.CONFLICT);
            }
            if (data.department_ids) {
                await (0, holiday_1.ensureHolidayDepartmentIdsExist)(this.db, data.department_ids, owner_id);
            }
            console.log({ existingHoliday });
            const updatedHoliday = await this.db.holidays.update({
                where: { id },
                data: {
                    ...(data.name ? { name: data.name } : {}),
                    ...(data.holiday_type ? { holiday_type: data.holiday_type } : {}),
                    ...(data.start_date ? { start_date: nextStartDate } : {}),
                    ...(data.end_date ? { end_date: nextEndDate } : {}),
                    ...(typeof data.is_recurring === 'boolean'
                        ? { is_recurring: data.is_recurring }
                        : {}),
                    ...(data.description !== undefined
                        ? { description: data.description }
                        : {}),
                    ...(data.department_ids !== undefined
                        ? {
                            departments: {
                                set: data.department_ids.map((id) => ({ id })),
                            },
                        }
                        : {}),
                },
                include: (0, holiday_1.includeHolidayDepartments)(),
            });
            this.notificationService.sendNotification({
                owner_id,
                title: updatedHoliday?.name,
                start_time: data?.start_date,
                end_time: data?.end_date,
                message: `The holiday schedule has been updated for the period between ${(0, utils_1.getUserTimzoneDate)(data.start_date)} and ${(0, utils_1.getUserTimzoneDate)(data.end_date)}. Please check the latest holiday information for any changes.`,
                type: entities_1.NotificationTypeEnum.holiday,
                reference_id: updatedHoliday?.id,
                include_owner: true,
                include_admins: true,
                department_ids: data?.department_ids ??
                    existingHoliday?.departments?.map((dept) => dept.id) ??
                    [],
                meta: {
                    holiday_id: updatedHoliday?.id,
                    holiday_type: updatedHoliday?.holiday_type,
                },
            });
            const { data: parsedHoliday } = holidays_dto_1.holidayResponseSchema.safeParse(updatedHoliday);
            return {
                data: parsedHoliday,
                message: 'Holiday updated successfully',
                status: common_1.HttpStatus.OK,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in updateHoliday: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
    async deleteHoliday(id, owner_id, ctx) {
        try {
            const permissions = await this.getPermissions(ctx);
            (0, permission_resolver_helpers_1.assertToggle)(permissions, 'holidays.access');
            (0, permission_resolver_helpers_1.assertScope)(permissions, 'holidays.delete');
            const existingHoliday = await this.db.holidays.findUnique({
                where: { id, owner_id },
            });
            if (!existingHoliday) {
                throw (0, utils_1.createGeneralError)('Holiday not found', 'holiday', common_1.HttpStatus.NOT_FOUND);
            }
            await this.db.holidays.delete({
                where: { id },
            });
            return {
                data: null,
                message: 'Holiday deleted successfully',
                status: common_1.HttpStatus.OK,
                details: null,
            };
        }
        catch (error) {
            config_1.logger.error(`Error in deleteHoliday: ${String(error)}`);
            if (error instanceof config_1.ErrorResponse) {
                throw error;
            }
            throw (0, utils_1.createGeneralError)(String(error), 'root', common_1.HttpStatus.BAD_REQUEST);
        }
    }
};
exports.HolidaysService = HolidaysService;
exports.HolidaysService = HolidaysService = __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])
], HolidaysService);
//# sourceMappingURL=holidays.service.js.map