"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); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrismaService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const entities_1 = require("../database/entities");
const logger_config_1 = require("../logger/logger.config");
class EntityAccessor {
    constructor(dataSource, entity) {
        this.dataSource = dataSource;
        this.entity = entity;
    }
    get repository() {
        return this.dataSource.getRepository(this.entity);
    }
    get entityName() {
        return this.repository.metadata.tableName;
    }
    translateScalar(value) {
        if (value === null || value === undefined)
            return value;
        if (value instanceof Date)
            return value;
        if (Array.isArray(value))
            return value.map((item) => this.translateScalar(item));
        if (typeof value !== 'object')
            return value;
        if (value.contains !== undefined) {
            return (0, typeorm_2.Like)(`%${String(value.contains)}%`);
        }
        if (value.in !== undefined) {
            return (0, typeorm_2.In)(value.in);
        }
        if (value.gte !== undefined) {
            return (0, typeorm_2.MoreThanOrEqual)(value.gte);
        }
        if (value.lte !== undefined) {
            return (0, typeorm_2.LessThanOrEqual)(value.lte);
        }
        if (value.gt !== undefined) {
            return (0, typeorm_2.MoreThan)(value.gt);
        }
        if (value.lt !== undefined) {
            return (0, typeorm_2.LessThan)(value.lt);
        }
        if (value.not !== undefined) {
            return this.translateScalar(value.not);
        }
        if (value.some !== undefined) {
            return this.translateScalar(value.some);
        }
        if (value.connect !== undefined) {
            return this.translateScalar(value.connect);
        }
        if (value.set !== undefined) {
            return this.translateScalar(value.set);
        }
        if (value.create !== undefined) {
            return this.translateScalar(value.create);
        }
        const translated = {};
        for (const [key, nestedValue] of Object.entries(value)) {
            translated[key] = this.translateScalar(nestedValue);
        }
        return translated;
    }
    translateWhere(where) {
        if (!where)
            return undefined;
        if (Array.isArray(where)) {
            return where.map((item) => this.translateWhere(item));
        }
        const { OR, AND, NOT, ...rest } = where;
        const translatedRest = this.translateScalar(rest);
        if (Array.isArray(OR) && OR.length > 0) {
            return OR.map((item) => this.translateWhere({ ...rest, ...item }));
        }
        if (Array.isArray(AND) && AND.length > 0) {
            return AND.map((item) => this.translateWhere({ ...rest, ...item }));
        }
        if (NOT && typeof NOT === 'object') {
            for (const [key, value] of Object.entries(NOT)) {
                translatedRest[key] = this.translateScalar({ not: value });
            }
        }
        return translatedRest;
    }
    translateOrder(orderBy) {
        if (!orderBy)
            return undefined;
        if (Array.isArray(orderBy)) {
            return orderBy.map((item) => this.translateOrder(item));
        }
        return orderBy;
    }
    collectRelations(shape) {
        if (!shape || typeof shape !== 'object') {
            return undefined;
        }
        const relations = {};
        for (const [key, value] of Object.entries(shape)) {
            if (key === '_count')
                continue;
            if (value === true) {
                relations[key] = true;
                continue;
            }
            if (value && typeof value === 'object') {
                const nested = value.select ??
                    value.include ??
                    value;
                const nestedRelations = this.collectRelations(nested);
                relations[key] =
                    nestedRelations && Object.keys(nestedRelations).length > 0
                        ? nestedRelations
                        : true;
            }
        }
        return Object.keys(relations).length > 0 ? relations : undefined;
    }
    normalizePersistValue(value) {
        if (value === null || value === undefined)
            return value;
        if (value instanceof Date)
            return value;
        if (Array.isArray(value))
            return value.map((item) => this.normalizePersistValue(item));
        if (typeof value !== 'object')
            return value;
        if (value.disconnect === true) {
            return null;
        }
        if (value.connect !== undefined) {
            return this.normalizePersistValue(value.connect);
        }
        if (value.set !== undefined) {
            return this.normalizePersistValue(value.set);
        }
        if (value.create !== undefined) {
            return this.normalizePersistValue(value.create);
        }
        const normalized = {};
        for (const [key, nestedValue] of Object.entries(value)) {
            normalized[key] = this.normalizePersistValue(nestedValue);
        }
        return normalized;
    }
    normalizePersistData(data) {
        const normalized = {};
        for (const [key, value] of Object.entries(data ?? {})) {
            if (this.entityName === 'events' && key === 'members') {
                continue;
            }
            normalized[key] = this.normalizePersistValue(value);
        }
        return normalized;
    }
    async reload(saved, args = {}) {
        const id = saved?.id;
        if (!id)
            return saved;
        const relations = this.collectRelations(args.include ?? args.select);
        const reloaded = await this.repository.findOne({
            where: { id },
            relations,
        });
        return reloaded ?? saved;
    }
    async syncEventMembers(eventId, payload) {
        if (this.entityName !== 'events' || !payload?.members)
            return;
        const memberRepository = this.dataSource.getRepository(entities_1.MemberEntity);
        const relationPayload = payload.members;
        let memberIds = [];
        if (Array.isArray(relationPayload.connect)) {
            memberIds = relationPayload.connect
                .map((item) => item.id)
                .filter(Boolean);
        }
        else if (Array.isArray(relationPayload.set)) {
            memberIds = relationPayload.set
                .map((item) => item.id)
                .filter(Boolean);
            const existingMembers = await memberRepository.find({
                where: { eventsId: eventId },
                select: { id: true },
            });
            const existingIds = existingMembers.map((member) => member.id);
            const removedIds = existingIds.filter((memberId) => !memberIds.includes(memberId));
            if (removedIds.length > 0) {
                await memberRepository.update({ id: (0, typeorm_2.In)(removedIds) }, { eventsId: null });
            }
        }
        if (memberIds.length > 0) {
            await memberRepository.update({ id: (0, typeorm_2.In)(memberIds) }, { eventsId: eventId });
        }
    }
    async findMany(args = {}) {
        const where = this.translateWhere(args.where);
        const order = this.translateOrder(args.orderBy);
        const relations = this.collectRelations(args.include ?? args.select);
        let rows = await this.repository.find({
            where,
            relations,
            order,
            skip: args.skip,
            take: args.take,
        });
        if (this.entityName === 'departments' &&
            args.include?._count?.select?.members) {
            const memberRepository = this.dataSource.getRepository(entities_1.MemberEntity);
            const departmentIds = rows.map((row) => row.id).filter(Boolean);
            const memberRows = departmentIds.length
                ? await memberRepository.find({
                    where: {
                        department_id: (0, typeorm_2.In)(departmentIds),
                        ...(args.where?.owner_id
                            ? { owner_id: args.where.owner_id }
                            : {}),
                    },
                    select: { department_id: true },
                })
                : [];
            const counts = memberRows.reduce((acc, member) => {
                if (!member.department_id)
                    return acc;
                acc[member.department_id] = (acc[member.department_id] ?? 0) + 1;
                return acc;
            }, {});
            rows = rows.map((row) => ({
                ...row,
                _count: {
                    members: counts[row.id] ?? 0,
                },
            }));
        }
        return rows;
    }
    async findFirst(args = {}) {
        const rows = await this.findMany({ ...args, take: 1 });
        return rows[0] ?? null;
    }
    async findUnique(args = {}) {
        return this.findFirst(args);
    }
    async count(args = {}) {
        const where = this.translateWhere(args.where);
        return this.repository.count({ where });
    }
    async create(args = {}) {
        const payload = this.normalizePersistData(args.data ?? {});
        const entity = this.repository.create(payload);
        const saved = await this.repository.save(entity);
        if (payload?.members && this.entityName === 'events') {
            await this.syncEventMembers(saved.id, payload);
        }
        return this.reload(saved, args);
    }
    async update(args = {}) {
        const existing = await this.findFirst({
            where: args.where,
            include: args.include,
            select: args.select,
        });
        if (!existing)
            return null;
        const payload = this.normalizePersistData(args.data ?? {});
        const merged = this.repository.merge(existing, payload);
        const saved = await this.repository.save(merged);
        if (payload?.members && this.entityName === 'events') {
            await this.syncEventMembers(saved.id, payload);
        }
        return this.reload(saved, args);
    }
    async delete(args = {}) {
        const existing = await this.findFirst({ where: args.where });
        if (!existing)
            return null;
        await this.repository.remove(existing);
        return existing;
    }
    async upsert(args = {}) {
        const existing = await this.findFirst({
            where: args.where,
            include: args.include,
            select: args.select,
        });
        if (existing) {
            return this.update({
                where: args.where,
                data: args.update,
                include: args.include,
                select: args.select,
            });
        }
        return this.create({
            data: { ...(args.create ?? {}) },
            include: args.include,
            select: args.select,
        });
    }
    async createMany(args = {}) {
        const rows = Array.isArray(args.data)
            ? args.data.map((item) => this.normalizePersistData(item))
            : [];
        if (rows.length === 0) {
            return { count: 0 };
        }
        await this.repository.insert(rows);
        return { count: rows.length };
    }
    async updateMany(args = {}) {
        const payload = this.normalizePersistData(args.data ?? {});
        const result = await this.repository.update(this.translateWhere(args.where), payload);
        return { count: result.affected ?? 0 };
    }
    async deleteMany(args = {}) {
        const result = await this.repository.delete(this.translateWhere(args.where));
        return { count: result.affected ?? 0 };
    }
    async groupBy(args = {}) {
        const groupFields = Array.isArray(args.by) ? args.by : [];
        if (groupFields.length === 0)
            return [];
        const rows = await this.findMany({ where: args.where });
        const groups = new Map();
        for (const row of rows) {
            const groupKey = groupFields
                .map((field) => String(row[field]))
                .join('::');
            const existing = groups.get(groupKey) ?? {
                ...groupFields.reduce((acc, field) => {
                    acc[field] = row[field];
                    return acc;
                }, {}),
                _count: { _all: 0 },
            };
            existing._count._all += 1;
            groups.set(groupKey, existing);
        }
        return Array.from(groups.values());
    }
}
let PrismaService = class PrismaService {
    constructor(dataSource) {
        this.dataSource = dataSource;
        this.owner = new EntityAccessor(this.dataSource, entities_1.OwnerEntity);
        this.company = new EntityAccessor(this.dataSource, entities_1.CompanyEntity);
        this.working_schedule = new EntityAccessor(this.dataSource, entities_1.WorkingScheduleEntity);
        this.leave_policy = new EntityAccessor(this.dataSource, entities_1.LeavePolicyEntity);
        this.members = new EntityAccessor(this.dataSource, entities_1.MemberEntity);
        this.attendances = new EntityAccessor(this.dataSource, entities_1.AttendanceEntity);
        this.leaves = new EntityAccessor(this.dataSource, entities_1.LeaveEntity);
        this.holidays = new EntityAccessor(this.dataSource, entities_1.HolidayEntity);
        this.events = new EntityAccessor(this.dataSource, entities_1.EventEntity);
        this.notices = new EntityAccessor(this.dataSource, entities_1.NoticeEntity);
        this.work_experiences = new EntityAccessor(this.dataSource, entities_1.WorkExperienceEntity);
        this.educations = new EntityAccessor(this.dataSource, entities_1.EducationEntity);
        this.departments = new EntityAccessor(this.dataSource, entities_1.DepartmentEntity);
        this.designations = new EntityAccessor(this.dataSource, entities_1.DesignationEntity);
        this.locations = new EntityAccessor(this.dataSource, entities_1.LocationEntity);
        this.employement_types = new EntityAccessor(this.dataSource, entities_1.EmployementTypeEntity);
        this.hiring_sources = new EntityAccessor(this.dataSource, entities_1.HiringSourceEntity);
    }
    async onModuleInit() {
        if (!this.dataSource.isInitialized) {
            await this.dataSource.initialize();
        }
        logger_config_1.logger.info('Connected to the database');
    }
    async onModuleDestroy() {
        if (this.dataSource.isInitialized) {
            await this.dataSource.destroy();
        }
        logger_config_1.logger.info('Disconnected from the database');
    }
};
exports.PrismaService = PrismaService;
exports.PrismaService = PrismaService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectDataSource)()),
    __metadata("design:paramtypes", [typeorm_2.DataSource])
], PrismaService);
//# sourceMappingURL=prisma.service.js.map