"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.RoleMappingRuleService = void 0;
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const typeorm_1 = require("@n8n/typeorm");
const bad_request_error_1 = require("../../errors/response-errors/bad-request.error");
const conflict_error_1 = require("../../errors/response-errors/conflict.error");
const not_found_error_1 = require("../../errors/response-errors/not-found.error");
const role_mapping_rule_validation_1 = require("./role-mapping-rule.validation");
let RoleMappingRuleService = class RoleMappingRuleService {
    constructor(roleMappingRuleRepository, roleRepository, projectRepository) {
        this.roleMappingRuleRepository = roleMappingRuleRepository;
        this.roleRepository = roleRepository;
        this.projectRepository = projectRepository;
    }
    async list(query) {
        const sortBy = query.sortBy ?? 'order:asc';
        const [sortField, sortDir] = sortBy.split(':');
        const direction = sortDir === 'desc' ? 'DESC' : 'ASC';
        const order = sortField === 'createdAt'
            ? { createdAt: direction, id: 'ASC' }
            : sortField === 'updatedAt'
                ? { updatedAt: direction, id: 'ASC' }
                : { order: direction, id: 'ASC' };
        const where = query.type ? { type: query.type } : {};
        const [entities, count] = await this.roleMappingRuleRepository.findAndCount({
            where,
            relations: ['projects', 'role'],
            order,
            skip: query.skip,
            take: query.take,
        });
        return {
            count,
            items: entities.map((entity) => this.toResponse(entity)),
        };
    }
    async create(dto) {
        const uniqueProjectIds = (0, role_mapping_rule_validation_1.assertAndNormalizeProjectIdsForRuleType)(dto.type, dto.projectIds, []);
        const role = await this.roleRepository.findOne({ where: { slug: dto.role } });
        if (!role) {
            throw new not_found_error_1.NotFoundError(`Could not find role with slug "${dto.role}"`);
        }
        (0, role_mapping_rule_validation_1.assertRoleCompatibleWithMappingType)(role, dto.type);
        await this.assertOrderAvailable(dto.type, dto.order);
        const projects = uniqueProjectIds.length > 0
            ? await this.projectRepository.findBy({ id: (0, typeorm_1.In)(uniqueProjectIds) })
            : [];
        if (projects.length !== uniqueProjectIds.length) {
            throw new bad_request_error_1.BadRequestError('One or more projects were not found');
        }
        const rule = new db_1.RoleMappingRule();
        rule.expression = dto.expression;
        rule.role = role;
        rule.type = dto.type;
        rule.order = dto.order;
        rule.projects = projects;
        const saved = await this.roleMappingRuleRepository.save(rule);
        await this.normalizeOrderForType(dto.type);
        const loaded = await this.roleMappingRuleRepository.findOneOrFail({
            where: { id: saved.id },
            relations: ['projects', 'role'],
        });
        return this.toResponse(loaded);
    }
    async patch(id, dto) {
        if (typeof id !== 'string' || id.length === 0) {
            throw new bad_request_error_1.BadRequestError('Rule id is required');
        }
        if (dto === undefined || dto === null || Object.keys(dto).length === 0) {
            throw new bad_request_error_1.BadRequestError('At least one field is required');
        }
        const rule = await this.roleMappingRuleRepository.findOne({
            where: { id },
            relations: ['projects', 'role'],
        });
        if (!rule) {
            throw new not_found_error_1.NotFoundError('Could not find role mapping rule');
        }
        const originalType = rule.type;
        const mergedType = dto.type ?? originalType;
        const mergedOrder = dto.order ?? rule.order;
        const mergedExpression = dto.expression ?? rule.expression;
        const mergedRoleSlug = dto.role ?? rule.role.slug;
        const fallbackProjectIds = rule.projects.map((p) => p.id);
        const uniqueProjectIds = (0, role_mapping_rule_validation_1.assertAndNormalizeProjectIdsForRuleType)(mergedType, dto.projectIds, fallbackProjectIds);
        const role = mergedRoleSlug === rule.role.slug
            ? rule.role
            : await this.roleRepository.findOne({ where: { slug: mergedRoleSlug } });
        if (!role) {
            throw new not_found_error_1.NotFoundError(`Could not find role with slug "${mergedRoleSlug}"`);
        }
        (0, role_mapping_rule_validation_1.assertRoleCompatibleWithMappingType)(role, mergedType);
        await this.assertOrderAvailable(mergedType, mergedOrder, id);
        const projects = uniqueProjectIds.length > 0
            ? await this.projectRepository.findBy({ id: (0, typeorm_1.In)(uniqueProjectIds) })
            : [];
        if (projects.length !== uniqueProjectIds.length) {
            throw new bad_request_error_1.BadRequestError('One or more projects were not found');
        }
        rule.expression = mergedExpression;
        rule.role = role;
        rule.type = mergedType;
        rule.order = mergedOrder;
        rule.projects = projects;
        await this.roleMappingRuleRepository.save(rule);
        await this.normalizeOrderForType(mergedType);
        if (originalType !== mergedType) {
            await this.normalizeOrderForType(originalType);
        }
        const loaded = await this.roleMappingRuleRepository.findOneOrFail({
            where: { id: rule.id },
            relations: ['projects', 'role'],
        });
        return this.toResponse(loaded);
    }
    async delete(id) {
        if (typeof id !== 'string' || id.length === 0) {
            throw new bad_request_error_1.BadRequestError('Rule id is required');
        }
        const rule = await this.roleMappingRuleRepository.findOne({ where: { id } });
        if (!rule) {
            throw new not_found_error_1.NotFoundError('Could not find role mapping rule');
        }
        const ruleType = rule.type;
        await this.roleMappingRuleRepository.remove(rule);
        await this.normalizeOrderForType(ruleType);
        return { ruleType };
    }
    async move(id, targetIndex) {
        if (typeof id !== 'string' || id.length === 0) {
            throw new bad_request_error_1.BadRequestError('Rule id is required');
        }
        const rule = await this.roleMappingRuleRepository.findOne({
            where: { id },
            relations: ['projects', 'role'],
        });
        if (!rule) {
            throw new not_found_error_1.NotFoundError('Could not find role mapping rule');
        }
        const type = rule.type;
        const all = await this.roleMappingRuleRepository.find({
            where: { type },
            select: ['id', 'order'],
            order: { order: 'ASC' },
        });
        const clampedIndex = Math.min(targetIndex, all.length - 1);
        const currentIndex = all.findIndex((r) => r.id === id);
        const reordered = [...all];
        reordered.splice(currentIndex, 1);
        reordered.splice(clampedIndex, 0, all[currentIndex]);
        await this.applyOrder(reordered.map((r) => r.id));
        const loaded = await this.roleMappingRuleRepository.findOneOrFail({
            where: { id },
            relations: ['projects', 'role'],
        });
        return this.toResponse(loaded);
    }
    async applyOrder(orderedIds) {
        if (orderedIds.length === 0)
            return;
        await this.roleMappingRuleRepository.manager.transaction(async (tx) => {
            const offset = orderedIds.length + 1000;
            for (let i = 0; i < orderedIds.length; i++) {
                await tx.update(db_1.RoleMappingRule, { id: orderedIds[i] }, { order: offset + i });
            }
            for (let i = 0; i < orderedIds.length; i++) {
                await tx.update(db_1.RoleMappingRule, { id: orderedIds[i] }, { order: i });
            }
        });
    }
    async normalizeOrderForType(type) {
        const rules = await this.roleMappingRuleRepository.find({
            where: { type },
            select: ['id', 'order'],
            order: { order: 'ASC' },
        });
        if (rules.length === 0)
            return;
        if (rules.every((r, i) => r.order === i))
            return;
        await this.applyOrder(rules.map((r) => r.id));
    }
    async assertOrderAvailable(type, order, excludeRuleId) {
        const existingAtOrder = await this.roleMappingRuleRepository.findOne({
            where: { type, order },
        });
        if (existingAtOrder && existingAtOrder.id !== excludeRuleId) {
            throw new conflict_error_1.ConflictError(`A role mapping rule already exists with type "${type}" and order ${order}. Use a different order value.`);
        }
    }
    toResponse(loaded) {
        return {
            id: loaded.id,
            expression: loaded.expression,
            role: loaded.role.slug,
            type: loaded.type,
            order: loaded.order,
            projectIds: loaded.projects.map((p) => p.id),
            createdAt: loaded.createdAt.toISOString(),
            updatedAt: loaded.updatedAt.toISOString(),
        };
    }
};
exports.RoleMappingRuleService = RoleMappingRuleService;
exports.RoleMappingRuleService = RoleMappingRuleService = __decorate([
    (0, di_1.Service)(),
    __metadata("design:paramtypes", [db_1.RoleMappingRuleRepository,
        db_1.RoleRepository,
        db_1.ProjectRepository])
], RoleMappingRuleService);
//# sourceMappingURL=role-mapping-rule.service.ee.js.map