import { ForgotPasswordRepository } from "../repositories/ForgotPasswordRepository.js";
import { UserRepository } from "../repositories/UserRepository.js";
import { forgotPasswordTemplate } from "../utils/mail/templates/forgot-password.js";
import mailService from "../utils/mail/mail.js";
import bcrypt from "bcryptjs";

export class ForgotPasswordService {
    constructor(
        private readonly forgotPasswordRepository = new ForgotPasswordRepository(),
        private readonly userRepository = new UserRepository()
    ) { }

    async sendOtp(email: string) {
        const user = await this.forgotPasswordRepository.findUserByEmail(email)

        if (!user) {
            throw new Error("User not found")
        }

        if (user.status == false) {
            throw new Error("User is inactive")
        }

        const otpData = await this.forgotPasswordRepository.createOrUpdateOtp(user.id)
        const appName = process.env.APP_NAME || "AVS Enterprises"
        const html = forgotPasswordTemplate(user.name, otpData.otp, appName);
        const mailResult = await mailService.send({
            to: email,
            subject: "Reset Password",
            html,
        });

        if (!mailResult.success) {
            throw new Error(mailResult.error || "Failed to send email");
        }

        return otpData
    }

    async verifyOtp(email: string, otp: string) {
        const otpData = await this.forgotPasswordRepository.getOtpByEmail(email)
        if (!otpData) {
            throw new Error("OTP not found")
        }

        if (otpData.attempts <= 0) {
            throw new Error("Maximum OTP verification attempts reached. Please request a new OTP.");
        }

        // Check expiry before validating the OTP value — an expired but wrong OTP
        // should not burn an attempt.
        if (otpData.expiresAt.getTime() < new Date().getTime()) {
            throw new Error("OTP has expired")
        }

        if (otpData.otp !== otp) {
            await this.forgotPasswordRepository.updateAttempt(otpData);
            throw new Error("Invalid OTP.");
        }

        return otpData
    }

    async resetPassword(email: string, otp: string, newPassword: string) {
        const otpData = await this.verifyOtp(email, otp);

        const hashedPassword = await bcrypt.hash(newPassword, 10);

        await this.userRepository.update(otpData.userId, {
            password: hashedPassword
        });

        await this.forgotPasswordRepository.deleteOtp(otpData.userId);

        return { message: "Password reset successfully" };
    }

}