"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 NestAuthUser_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NestAuthUser = void 0;
const typeorm_1 = require("typeorm");
const argon2_1 = require("@node-rs/argon2");
const auth_config_service_1 = require("../../core/services/auth-config.service");
const identity_entity_1 = require("./identity.entity");
const session_entity_1 = require("../../session/entities/session.entity");
const otp_entity_1 = require("../../auth/entities/otp.entity");
const mfa_secret_entity_1 = require("../../auth/entities/mfa-secret.entity");
const user_access_entity_1 = require("./user-access.entity");
const auth_constants_1 = require("../../auth.constants");
const utils_1 = require("../../utils");
const platform_access_entity_1 = require("./platform-access.entity");
let NestAuthUser = NestAuthUser_1 = class NestAuthUser extends typeorm_1.BaseEntity {
    id;
    email;
    emailVerifiedAt;
    phone;
    phoneVerifiedAt;
    passwordHash;
    isActive;
    metadata;
    isMfaEnabled;
    mfaRecoveryCode;
    identities;
    mfaSecrets;
    sessions;
    otps;
    userAccesses;
    platformAccess;
    createdAt;
    updatedAt;
    updateTenantFields() {
        if (this.email) {
            this.email = this.email.toLowerCase().trim();
        }
    }
    async getUserAccess(tenantId = null, createIfNotExists = false, manager) {
        const repo = manager
            ? manager.getRepository(user_access_entity_1.NestAuthUserAccess)
            : user_access_entity_1.NestAuthUserAccess.getRepository();
        const existingUserAccess = await repo.findOne({
            where: { userId: this.id, tenantId: tenantId ? (0, typeorm_1.Equal)(tenantId) : (0, typeorm_1.IsNull)() }
        });
        if (existingUserAccess) {
            return existingUserAccess;
        }
        if (createIfNotExists) {
            const userAccess = repo.create({ userId: this.id, tenantId });
            await repo.save(userAccess);
            return userAccess;
        }
        return null;
    }
    async getPlatformAccess(createIfNotExists = false, manager) {
        const repo = manager
            ? manager.getRepository(platform_access_entity_1.NestAuthPlatformAccess)
            : platform_access_entity_1.NestAuthPlatformAccess.getRepository();
        const existingPlatformAccess = await repo.findOne({
            where: { userId: this.id }
        });
        if (existingPlatformAccess) {
            return existingPlatformAccess;
        }
        if (createIfNotExists) {
            const platformAccess = repo.create({ userId: this.id });
            await repo.save(platformAccess);
            return platformAccess;
        }
        return null;
    }
    async findOrCreateIdentity(provider, providerId, manager) {
        const repo = manager
            ? manager.getRepository(identity_entity_1.NestAuthIdentity)
            : identity_entity_1.NestAuthIdentity.getRepository();
        const existingIdentity = await repo.findOne({
            where: { provider, providerId, userId: this.id }
        });
        if (existingIdentity) {
            return existingIdentity;
        }
        const identity = repo.create({
            provider,
            providerId,
            userId: this.id,
        });
        return repo.save(identity);
    }
    async updateOrCreateIdentity(provider, data, manager) {
        const repo = manager
            ? manager.getRepository(identity_entity_1.NestAuthIdentity)
            : identity_entity_1.NestAuthIdentity.getRepository();
        const existingIdentity = await repo.findOne({
            where: { provider, userId: this.id },
        });
        if (existingIdentity) {
            Object.assign(existingIdentity, data);
            return repo.save(existingIdentity);
        }
        const newIdentity = repo.create({
            provider,
            userId: this.id,
            ...data,
        });
        return repo.save(newIdentity);
    }
    async updateEmail(newEmail, manager) {
        const normalized = newEmail ? newEmail.toLowerCase().trim() : null;
        const previousEmail = this.email?.toLowerCase().trim() ?? null;
        this.email = normalized ?? undefined;
        if (previousEmail !== normalized) {
            this.emailVerifiedAt = null;
        }
        const identityRepo = manager
            ? manager.getRepository(identity_entity_1.NestAuthIdentity)
            : identity_entity_1.NestAuthIdentity.getRepository();
        const userRepo = manager
            ? manager.getRepository(NestAuthUser_1)
            : NestAuthUser_1.getRepository();
        if (normalized) {
            await this.updateOrCreateIdentity(auth_constants_1.EMAIL_AUTH_PROVIDER, { providerId: normalized }, manager);
        }
        else {
            const identity = await identityRepo.findOne({
                where: { userId: this.id, provider: auth_constants_1.EMAIL_AUTH_PROVIDER },
            });
            if (identity) {
                await identityRepo.remove(identity);
            }
        }
        await userRepo.save(this);
    }
    async updatePhone(newPhone, manager) {
        const value = (0, utils_1.normalizedPhone)(newPhone);
        const previousPhone = (0, utils_1.normalizedPhone)(this.phone) ?? null;
        this.phone = value ?? undefined;
        if (previousPhone !== value) {
            this.phoneVerifiedAt = null;
        }
        const identityRepo = manager
            ? manager.getRepository(identity_entity_1.NestAuthIdentity)
            : identity_entity_1.NestAuthIdentity.getRepository();
        const userRepo = manager
            ? manager.getRepository(NestAuthUser_1)
            : NestAuthUser_1.getRepository();
        if (value) {
            await this.updateOrCreateIdentity(auth_constants_1.PHONE_AUTH_PROVIDER, { providerId: value }, manager);
        }
        else {
            const identity = await identityRepo.findOne({
                where: { userId: this.id, provider: auth_constants_1.PHONE_AUTH_PROVIDER },
            });
            if (identity) {
                await identityRepo.remove(identity);
            }
        }
        await userRepo.save(this);
    }
    async validatePassword(password) {
        let passwordHash = this.passwordHash;
        if (!this.passwordHash) {
            const user = await NestAuthUser_1.createQueryBuilder('user').select('user.passwordHash').where('user.id = :id', { id: this.id }).getOne();
            if (!user?.passwordHash) {
                return false;
            }
            passwordHash = user.passwordHash;
        }
        ;
        const options = auth_config_service_1.AuthConfigService.getOptions();
        const hasCustomHash = !!options.password?.hash;
        const hasCustomVerify = !!options.password?.verify;
        if (hasCustomHash !== hasCustomVerify) {
            throw new Error('password.hash and password.verify must be provided together');
        }
        if (hasCustomVerify) {
            return await options.password.verify(password, passwordHash);
        }
        try {
            return await (0, argon2_1.verify)(passwordHash, password);
        }
        catch (error) {
            return false;
        }
    }
    async setPassword(password) {
        const options = auth_config_service_1.AuthConfigService.getOptions();
        const hasCustomHash = !!options.password?.hash;
        const hasCustomVerify = !!options.password?.verify;
        if (hasCustomHash !== hasCustomVerify) {
            throw new Error('password.hash and password.verify must be provided together');
        }
        if (hasCustomHash) {
            this.passwordHash = await options.password.hash(password);
            return;
        }
        this.passwordHash = await (0, argon2_1.hash)(password, {
            algorithm: 2,
            memoryCost: options.password?.argon2?.memoryCost ?? 65536,
            timeCost: options.password?.argon2?.timeCost ?? 3,
            parallelism: options.password?.argon2?.parallelism ?? 4,
        });
    }
};
exports.NestAuthUser = NestAuthUser;
__decorate([
    (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
    __metadata("design:type", String)
], NestAuthUser.prototype, "id", void 0);
__decorate([
    (0, typeorm_1.Column)({ nullable: true }),
    (0, typeorm_1.Index)(),
    __metadata("design:type", String)
], NestAuthUser.prototype, "email", void 0);
__decorate([
    (0, typeorm_1.Column)({ nullable: true }),
    (0, typeorm_1.Index)(),
    __metadata("design:type", Date)
], NestAuthUser.prototype, "emailVerifiedAt", void 0);
__decorate([
    (0, typeorm_1.Column)({ nullable: true }),
    (0, typeorm_1.Index)(),
    __metadata("design:type", String)
], NestAuthUser.prototype, "phone", void 0);
__decorate([
    (0, typeorm_1.Column)({ nullable: true }),
    (0, typeorm_1.Index)(),
    __metadata("design:type", Date)
], NestAuthUser.prototype, "phoneVerifiedAt", void 0);
__decorate([
    (0, typeorm_1.Column)({ nullable: true, select: false }),
    __metadata("design:type", String)
], NestAuthUser.prototype, "passwordHash", void 0);
__decorate([
    (0, typeorm_1.Column)({ default: true }),
    __metadata("design:type", Boolean)
], NestAuthUser.prototype, "isActive", void 0);
__decorate([
    (0, typeorm_1.Column)({ type: 'simple-json', nullable: true, default: '{}' }),
    __metadata("design:type", Object)
], NestAuthUser.prototype, "metadata", void 0);
__decorate([
    (0, typeorm_1.Column)({ default: false }),
    __metadata("design:type", Boolean)
], NestAuthUser.prototype, "isMfaEnabled", void 0);
__decorate([
    (0, typeorm_1.Column)({ nullable: true }),
    __metadata("design:type", String)
], NestAuthUser.prototype, "mfaRecoveryCode", void 0);
__decorate([
    (0, typeorm_1.OneToMany)(() => identity_entity_1.NestAuthIdentity, identity => identity.user),
    __metadata("design:type", Array)
], NestAuthUser.prototype, "identities", void 0);
__decorate([
    (0, typeorm_1.OneToMany)(() => mfa_secret_entity_1.NestAuthMFASecret, mfaSecret => mfaSecret.user),
    __metadata("design:type", Array)
], NestAuthUser.prototype, "mfaSecrets", void 0);
__decorate([
    (0, typeorm_1.OneToMany)(() => session_entity_1.NestAuthSession, session => session.user),
    __metadata("design:type", Array)
], NestAuthUser.prototype, "sessions", void 0);
__decorate([
    (0, typeorm_1.OneToMany)(() => otp_entity_1.NestAuthOTP, otp => otp.user),
    __metadata("design:type", Array)
], NestAuthUser.prototype, "otps", void 0);
__decorate([
    (0, typeorm_1.OneToMany)(() => user_access_entity_1.NestAuthUserAccess, access => access.user),
    __metadata("design:type", Array)
], NestAuthUser.prototype, "userAccesses", void 0);
__decorate([
    (0, typeorm_1.OneToOne)(() => platform_access_entity_1.NestAuthPlatformAccess, access => access.user),
    __metadata("design:type", platform_access_entity_1.NestAuthPlatformAccess)
], NestAuthUser.prototype, "platformAccess", void 0);
__decorate([
    (0, typeorm_1.CreateDateColumn)(),
    __metadata("design:type", Date)
], NestAuthUser.prototype, "createdAt", void 0);
__decorate([
    (0, typeorm_1.UpdateDateColumn)(),
    __metadata("design:type", Date)
], NestAuthUser.prototype, "updatedAt", void 0);
__decorate([
    (0, typeorm_1.BeforeInsert)(),
    (0, typeorm_1.BeforeUpdate)(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], NestAuthUser.prototype, "updateTenantFields", null);
exports.NestAuthUser = NestAuthUser = NestAuthUser_1 = __decorate([
    (0, typeorm_1.Entity)('nest_auth_users')
], NestAuthUser);
//# sourceMappingURL=user.entity.js.map