"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.AuthService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const jwt_1 = require("@nestjs/jwt");
const bcrypt = require("bcrypt");
const crypto = require("crypto");
const user_entity_1 = require("./entities/user.entity");
const user_profile_entity_1 = require("./entities/user-profile.entity");
const refresh_token_entity_1 = require("./entities/refresh-token.entity");
const user_verification_entity_1 = require("./entities/user-verification.entity");
const role_entity_1 = require("./entities/role.entity");
const mail_service_1 = require("../mail/mail.service");
let AuthService = class AuthService {
    constructor(userRepository, profileRepository, refreshTokenRepository, verificationRepository, roleRepository, jwtService, mailService) {
        this.userRepository = userRepository;
        this.profileRepository = profileRepository;
        this.refreshTokenRepository = refreshTokenRepository;
        this.verificationRepository = verificationRepository;
        this.roleRepository = roleRepository;
        this.jwtService = jwtService;
        this.mailService = mailService;
    }
    generateReferralCode() {
        return crypto.randomBytes(5).toString("hex").toUpperCase();
    }
    generateRefreshTokenValue() {
        return crypto.randomBytes(48).toString("hex");
    }
    async generateTokens(user) {
        const role = user.role
            ? typeof user.role === "string"
                ? user.role
                : user.role.name
            : null;
        const isAdmin = user.role ? !!user.role.is_admin : false;
        const accessToken = this.jwtService.sign({
            sub: user.id,
            email: user.email,
            role,
            isAdmin,
        });
        const refreshValue = this.generateRefreshTokenValue();
        const refresh = this.refreshTokenRepository.create({
            token: crypto.createHash("sha256").update(refreshValue).digest("hex"),
            user_id: user.id,
            expires_at: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        });
        await this.refreshTokenRepository.save(refresh);
        return { accessToken, refreshToken: `${refresh.id}.${refreshValue}` };
    }
    async findOrCreateGoogleUser(profile) {
        let user = await this.userRepository.findOne({
            where: { email: profile.email },
            relations: ["profile"],
        });
        if (!user) {
            const nameParts = profile.name.split(" ");
            const firstName = nameParts[0] || "";
            const lastName = nameParts.slice(1).join(" ") || "";
            user = this.userRepository.create({
                email: profile.email,
                password_hash: "",
                provider: "google",
                is_verified: true,
                status: "active",
                referral_code: this.generateReferralCode(),
            });
            const savedUser = await this.userRepository.save(user);
            const userProfile = this.profileRepository.create({
                user_id: savedUser.id,
                first_name: firstName,
                last_name: lastName,
                avatar_url: profile.picture ?? undefined,
            });
            await this.profileRepository.save(userProfile);
            savedUser.profile = userProfile;
            user = savedUser;
        }
        return user;
    }
    async register(dto) {
        const existingUser = await this.userRepository.findOne({
            where: { email: dto.correo },
        });
        if (existingUser) {
            throw new common_1.ConflictException("El correo ya está registrado");
        }
        if (dto.ruc) {
            const existingRuc = await this.profileRepository.findOne({
                where: { ruc: dto.ruc },
            });
            if (existingRuc) {
                throw new common_1.ConflictException("El RUC ya está registrado");
            }
        }
        const salt = await bcrypt.genSalt(12);
        const hashedPassword = await bcrypt.hash(dto.contrasena, salt);
        const verificationCode = Math.floor(100000 + Math.random() * 900000).toString();
        let roleId;
        if (dto.accountType) {
            const roleName = dto.accountType === "Quiero vender" ? "vendedor" : "comprador";
            let role = await this.roleRepository.findOne({ where: { name: roleName } });
            if (!role) {
                role = await this.roleRepository.save(this.roleRepository.create({ name: roleName, description: `Rol automático: ${dto.accountType}` }));
            }
            roleId = role.id;
        }
        let referredBy;
        if (dto.codigoReferidos) {
            const referrer = await this.userRepository.findOne({
                where: { referral_code: dto.codigoReferidos },
            });
            if (referrer) {
                referredBy = referrer.id;
            }
        }
        try {
            const user = this.userRepository.create({
                email: dto.correo,
                password_hash: hashedPassword,
                phone: dto.telefono,
                role_id: roleId,
                status: "active",
                referral_code: this.generateReferralCode(),
                referred_by: referredBy,
                is_verified: false,
            });
            const savedUser = await this.userRepository.save(user);
            const verification = this.verificationRepository.create({
                user_id: savedUser.id,
                verification_type: "email",
                verification_status: "pending",
                request_payload: { email: dto.correo },
                response_payload: {
                    code: verificationCode,
                    expires_at: new Date(Date.now() + 15 * 60 * 1000).toISOString(),
                },
            });
            await this.verificationRepository.save(verification);
            const profile = this.profileRepository.create({
                user_id: savedUser.id,
                first_name: dto.nombre,
                last_name: dto.apellidos,
                birth_date: dto.fechaNacimiento,
                document_type: "DNI",
                document_number: dto.dni,
                how_found_us: dto.comoNosEncontraste,
                ruc: dto.ruc,
                razon_social: dto.razonSocial,
                account_type: dto.accountType,
            });
            await this.profileRepository.save(profile);
            this.mailService
                .sendVerificationCode(dto.correo, verificationCode, dto.nombre)
                .catch(() => { });
            return {
                message: "Registro exitoso. Revisa tu correo para verificar tu cuenta.",
            };
        }
        catch (error) {
            if (error instanceof common_1.ConflictException)
                throw error;
            if (error.code === "23505") {
                throw new common_1.ConflictException("Uno de los datos ya está registrado (correo, RUC o DNI)");
            }
            throw new common_1.InternalServerErrorException("Error al registrar el usuario");
        }
    }
    async verifyEmail(dto) {
        const user = await this.userRepository.findOne({
            where: { email: dto.email },
            relations: ["profile", "role"],
        });
        if (!user) {
            throw new common_1.BadRequestException("Usuario no encontrado");
        }
        if (user.is_verified) {
            const tokens = await this.generateTokens(user);
            const { password_hash: _, ...result } = user;
            return {
                message: "La cuenta ya está verificada",
                ...tokens,
                user: result,
            };
        }
        const verification = await this.verificationRepository.findOne({
            where: {
                user_id: user.id,
                verification_type: "email",
                verification_status: "pending",
            },
            order: { created_at: "DESC" },
        });
        if (!verification) {
            throw new common_1.BadRequestException("No hay verificación pendiente. Solicita un nuevo código.");
        }
        const storedCode = verification.response_payload?.code;
        const expiresAt = verification.response_payload?.expires_at;
        if (storedCode !== dto.code) {
            throw new common_1.BadRequestException("Código de verificación inválido");
        }
        if (expiresAt && new Date(expiresAt) < new Date()) {
            throw new common_1.BadRequestException("El código de verificación expiró. Solicita uno nuevo.");
        }
        verification.verification_status = "verified";
        verification.verified_at = new Date();
        verification.verified_data = { email: dto.email };
        await this.verificationRepository.save(verification);
        user.is_verified = true;
        await this.userRepository.save(user);
        const tokens = await this.generateTokens(user);
        const { password_hash: _, ...result } = user;
        return {
            message: "Cuenta verificada exitosamente",
            ...tokens,
            user: result,
        };
    }
    async login(dto) {
        const user = await this.userRepository.findOne({
            where: [{ email: dto.credential }, { phone: dto.credential }],
            relations: ["profile", "role"],
        });
        if (!user) {
            throw new common_1.UnauthorizedException("Credenciales inválidas");
        }
        const isPasswordValid = await bcrypt.compare(dto.contrasena, user.password_hash);
        if (!isPasswordValid) {
            throw new common_1.UnauthorizedException("Credenciales inválidas");
        }
        if (!user.is_verified) {
            throw new common_1.UnauthorizedException("Cuenta no verificada. Revisa tu correo para verificar tu cuenta.");
        }
        const { password_hash: _, ...result } = user;
        const tokens = await this.generateTokens(user);
        return {
            message: "Inicio de sesión exitoso",
            ...tokens,
            user: result,
        };
    }
    async googleLogin(profile) {
        const user = await this.findOrCreateGoogleUser(profile);
        const { password_hash: _, ...result } = user;
        const tokens = await this.generateTokens(user);
        return {
            message: "Inicio de sesión con Google exitoso",
            ...tokens,
            user: result,
        };
    }
    async refreshAccessToken(refreshToken) {
        const [id, value] = refreshToken.split(".");
        if (!id || !value) {
            throw new common_1.UnauthorizedException("Refresh token inválido");
        }
        const hashedValue = crypto.createHash("sha256").update(value).digest("hex");
        const stored = await this.refreshTokenRepository.findOne({
            where: {
                id,
                token: hashedValue,
                is_revoked: false,
                expires_at: (0, typeorm_2.MoreThan)(new Date()),
            },
            relations: ["user", "user.profile"],
        });
        if (!stored) {
            throw new common_1.UnauthorizedException("Refresh token inválido o expirado");
        }
        stored.is_revoked = true;
        await this.refreshTokenRepository.save(stored);
        const user = stored.user;
        const { password_hash: _, ...result } = user;
        const tokens = await this.generateTokens(user);
        return {
            message: "Token renovado exitosamente",
            ...tokens,
            user: result,
        };
    }
    async logout(refreshToken) {
        const [id] = refreshToken.split(".");
        if (id) {
            await this.refreshTokenRepository.update(id, { is_revoked: true });
        }
        return { message: "Sesión cerrada exitosamente" };
    }
    async getProfile(userId) {
        const user = await this.userRepository.findOne({
            where: { id: userId },
            relations: ["profile", "role"],
        });
        if (!user) {
            throw new common_1.UnauthorizedException("Usuario no encontrado");
        }
        const { password_hash: _, ...result } = user;
        return result;
    }
    async selectPlan(userId, planId) {
        await this.profileRepository.update({ user_id: userId }, { plan_id: planId });
        await this.userRepository.query(`INSERT INTO seller_plans (user_id, plan_id, status, payment_status, starts_at, ends_at)
       SELECT $1, $2, 'active', 'pending', NOW(), NOW() + (COALESCE(duration_days, 30) || ' days')::INTERVAL FROM plans WHERE id = $2`, [userId, planId]);
        return { message: "Plan activado exitosamente" };
    }
    async saveBankAccount(userId, dto) {
        const result = await this.userRepository.query(`INSERT INTO bank_accounts (user_id, bank_name, account_number) VALUES ($1, $2, $3) RETURNING *`, [userId, dto.bank_name, dto.account_number]);
        return result[0];
    }
    async submitPayment(userId, dto) {
        const sp = await this.userRepository.query(`SELECT id FROM seller_plans WHERE user_id = $1 AND payment_status = 'pending' ORDER BY created_at DESC LIMIT 1`, [userId]);
        const sellerPlanId = sp[0]?.id;
        await this.userRepository.query(`INSERT INTO plan_payments (seller_plan_id, user_id, amount, payment_proof, status) VALUES ($1, $2, $3, $4, 'pending')`, [sellerPlanId, userId, dto.amount, dto.operation_number]);
        await this.userRepository.update(userId, { status: "pending_approval" });
        return { message: "Comprobante enviado. Tu cuenta está pendiente de aprobación." };
    }
};
exports.AuthService = AuthService;
exports.AuthService = AuthService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(user_entity_1.User)),
    __param(1, (0, typeorm_1.InjectRepository)(user_profile_entity_1.UserProfile)),
    __param(2, (0, typeorm_1.InjectRepository)(refresh_token_entity_1.RefreshToken)),
    __param(3, (0, typeorm_1.InjectRepository)(user_verification_entity_1.UserVerification)),
    __param(4, (0, typeorm_1.InjectRepository)(role_entity_1.Role)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        jwt_1.JwtService,
        mail_service_1.MailService])
], AuthService);
//# sourceMappingURL=auth.service.js.map