"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);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MenuService = void 0;
const api_1 = require("@hed-hog/api");
const api_locale_1 = require("@hed-hog/api-locale");
const api_pagination_1 = require("@hed-hog/api-pagination");
const api_prisma_1 = require("@hed-hog/api-prisma");
const common_1 = require("@nestjs/common");
let MenuService = class MenuService {
    constructor(prismaService, paginationService) {
        this.prismaService = prismaService;
        this.paginationService = paginationService;
    }
    async ensureValidParent(locale, currentMenuId, parentId) {
        if (parentId == null) {
            return;
        }
        const parent = await this.prismaService.menu.findUnique({
            where: { id: parentId },
            select: { id: true, menu_id: true },
        });
        if (!parent) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('menuNotFound', locale, 'Menu not found.'));
        }
        if (currentMenuId != null && parentId === currentMenuId) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('menuInvalidParent', locale, 'A menu cannot be its own parent.'));
        }
        if (currentMenuId == null) {
            return;
        }
        const visited = new Set([currentMenuId]);
        let cursor = parent;
        while (cursor) {
            if (visited.has(cursor.id)) {
                throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('menuInvalidParent', locale, 'You cannot move a menu inside itself or one of its descendants.'));
            }
            visited.add(cursor.id);
            if (cursor.menu_id == null) {
                break;
            }
            cursor = await this.prismaService.menu.findUnique({
                where: { id: cursor.menu_id },
                select: { id: true, menu_id: true },
            });
        }
    }
    async updateScreens(locale, menuId, data) {
        const menuExists = await this.prismaService.menu.count({
            where: {
                id: menuId,
            },
        });
        if (!menuExists) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('menuNotFound', locale, 'Menu not found.'));
        }
        await this.prismaService.menu_screen.deleteMany({
            where: {
                menu_id: menuId,
            },
        });
        return this.prismaService.menu_screen.createMany({
            data: data.ids.map((screenId) => ({
                menu_id: menuId,
                screen_id: screenId,
            })),
            skipDuplicates: true,
        });
    }
    async updateRoles(locale, menuId, data) {
        const menuExists = await this.prismaService.menu.count({
            where: {
                id: menuId,
            },
        });
        if (!menuExists) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('menuNotFound', locale, 'Menu not found.'));
        }
        await this.prismaService.role_menu.deleteMany({
            where: {
                menu_id: menuId,
            },
        });
        return this.prismaService.role_menu.createMany({
            data: data.ids.map((roleId) => ({
                menu_id: menuId,
                role_id: roleId,
            })),
            skipDuplicates: true,
        });
    }
    async listScreens(locale, menuId, paginationParams) {
        const menuExists = await this.prismaService.menu.count({
            where: {
                id: menuId,
            },
        });
        if (!menuExists) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('menuNotFound', locale, 'Menu not found.'));
        }
        return this.paginationService.paginate(this.prismaService.screen, paginationParams, {
            include: {
                screen_locale: {
                    where: {
                        locale: {
                            code: locale,
                        },
                    },
                    select: {
                        name: true,
                    },
                },
                menu_screen: {
                    where: {
                        menu_id: menuId,
                    },
                    select: {
                        screen_id: true,
                        menu_id: true,
                    },
                },
            },
        }, 'screen_locale');
    }
    async listRoles(locale, menuId, paginationParams) {
        const menuExists = await this.prismaService.menu.count({
            where: {
                id: menuId,
            },
        });
        if (!menuExists) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('menuNotFound', locale, 'Menu not found.'));
        }
        return this.paginationService.paginate(this.prismaService.role, paginationParams, {
            include: {
                role_locale: {
                    where: {
                        locale: {
                            code: locale,
                        },
                    },
                    select: {
                        name: true,
                        description: true,
                    },
                },
                role_menu: {
                    where: {
                        menu_id: menuId,
                    },
                    select: {
                        role_id: true,
                        menu_id: true,
                    },
                },
                role_user: {
                    select: {
                        user_id: true,
                        user: {
                            select: {
                                id: true,
                                name: true,
                                user_identifier: {
                                    where: {
                                        type: 'email',
                                    },
                                    select: {
                                        value: true,
                                    },
                                    take: 1,
                                },
                            },
                        },
                    },
                },
            },
        }, 'role_locale');
    }
    async getMenus(locale, userId, menuId = 0) {
        const userExists = await this.prismaService.user.count({
            where: {
                id: userId,
            },
        });
        if (!userExists) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('userNotFound', locale, 'User not found.'));
        }
        if (menuId === 0) {
            menuId = null;
        }
        let menu = (await this.prismaService.menu.findMany({
            where: {
                menu_id: menuId,
                role_menu: {
                    some: {
                        role: {
                            role_user: {
                                some: {
                                    user_id: userId,
                                },
                            },
                        },
                    },
                },
            },
            orderBy: {
                order: 'asc',
            },
            include: {
                menu_locale: {
                    where: {
                        locale: {
                            code: locale,
                        },
                    },
                    select: {
                        name: true,
                    },
                },
            },
        }));
        menu = menu.map((m) => (0, api_1.itemTranslations)('menu_locale', m));
        for (let i = 0; i < menu.length; i++) {
            menu[i].menu = await this.getMenus(locale, userId, menu[i].id);
        }
        return menu;
    }
    async getSystemMenu(locale, userId) {
        return this.getMenus(locale, userId);
    }
    async stats() {
        const total = await this.prismaService.menu.count();
        return { total };
    }
    async listAll(locale) {
        const menus = await this.prismaService.menu.findMany({
            orderBy: { order: 'asc' },
            include: {
                menu_locale: {
                    where: { locale: { code: locale } },
                    select: { name: true },
                },
                _count: {
                    select: {
                        role_menu: true,
                    },
                },
            },
        });
        return menus.map((m) => (0, api_1.itemTranslations)('menu_locale', m));
    }
    async list(locale, paginationParams) {
        const search = paginationParams.search;
        const whereOr = [];
        if (search) {
            whereOr.push({ url: { contains: search, mode: 'insensitive' } }, { icon: { contains: search, mode: 'insensitive' } }, { slug: { contains: search, mode: 'insensitive' } }, {
                menu_locale: {
                    some: {
                        name: { contains: search, mode: 'insensitive' },
                        locale: { code: locale },
                    },
                },
            });
        }
        return this.paginationService.paginate(this.prismaService.menu, paginationParams, {
            where: whereOr.length ? { OR: whereOr } : {},
            include: {
                menu_locale: {
                    where: {
                        locale: {
                            code: locale,
                        },
                    },
                    select: {
                        name: true,
                    },
                },
                _count: {
                    select: {
                        role_menu: true,
                    },
                },
            },
        }, 'menu_locale');
    }
    async get(locale, menuId) {
        const menu = await this.prismaService.menu.findUnique({
            where: { id: menuId },
            include: {
                menu_locale: {
                    include: {
                        locale: {
                            select: { code: true },
                        },
                    },
                },
            },
        });
        if (!menu) {
            throw new common_1.NotFoundException((0, api_locale_1.getLocaleText)('menuNotFound', locale, 'Menu not found.'));
        }
        return menu;
    }
    async create(_locale, { slug, url, icon, order, menu_id, locale }) {
        await this.ensureValidParent(_locale, null, menu_id);
        const created = await this.prismaService.menu.create({
            data: { slug, url, icon, order, menu_id },
        });
        if (locale && typeof locale === 'object') {
            for (const [localeCode, name] of Object.entries(locale)) {
                const localeRecord = await this.prismaService.locale.findUnique({
                    where: { code: localeCode },
                });
                if (localeRecord && name) {
                    await this.prismaService.menu_locale.create({
                        data: {
                            menu_id: created.id,
                            locale_id: localeRecord.id,
                            name: name,
                        },
                    });
                }
            }
        }
        return created;
    }
    async update(locale, { id, data }) {
        const menuExists = await this.prismaService.menu.count({
            where: {
                id,
            },
        });
        await this.ensureValidParent(locale, id, data.menu_id);
        if (!menuExists) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('menuNotFound', locale, 'Menu not found.'));
        }
        const { locale: localeData } = data, menuData = __rest(data, ["locale"]);
        const result = await this.prismaService.menu.update({
            where: { id },
            data: menuData
        });
        if (localeData) {
            for (const [localeCode, translation] of Object.entries(localeData)) {
                const localeRecord = await this.prismaService.locale.findUnique({
                    where: { code: localeCode },
                });
                if (localeRecord) {
                    const existingTranslation = await this.prismaService.menu_locale.findFirst({
                        where: {
                            menu_id: id,
                            locale_id: localeRecord.id,
                        },
                    });
                    if (existingTranslation) {
                        await this.prismaService.menu_locale.update({
                            where: { id: existingTranslation.id },
                            data: { name: translation },
                        });
                    }
                    else {
                        await this.prismaService.menu_locale.create({
                            data: {
                                menu_id: id,
                                locale_id: localeRecord.id,
                                name: translation,
                            },
                        });
                    }
                }
            }
        }
        return result;
    }
    async delete(locale, { ids }) {
        if (ids == undefined || ids == null) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('deleteItemsRequired', locale, 'You must select at least one menu to delete.'));
        }
        const count = await this.prismaService.menu.count({
            where: {
                id: {
                    in: ids,
                },
            },
        });
        if (count !== ids.length) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('invalidIds', locale, 'Invalid IDs.'));
        }
        return this.prismaService.menu.deleteMany({
            where: {
                id: {
                    in: ids,
                },
            },
        });
    }
    async updateOrder(locale, { ids }) {
        const count = await this.prismaService.menu.count({
            where: {
                id: {
                    in: ids,
                },
            },
        });
        if (count !== ids.length) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('invalidIds', locale, 'Invalid IDs.'));
        }
        await Promise.all(ids.map((id, index) => this.prismaService.menu.update({
            where: { id },
            data: { order: index + 1 },
        })));
    }
};
exports.MenuService = MenuService;
exports.MenuService = MenuService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => api_prisma_1.PrismaService))),
    __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => api_pagination_1.PaginationService))),
    __metadata("design:paramtypes", [api_prisma_1.PrismaService,
        api_pagination_1.PaginationService])
], MenuService);
//# sourceMappingURL=menu.service.js.map