import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import nodemailer from "nodemailer";
import crypto from "crypto";
import { DatabaseService } from "./databaseService";
import type { User } from "../entities/User";

const dbService = DatabaseService.getInstance();
const OTPs: Record<string, string> = {};

const transporter = nodemailer.createTransport({
  service: "gmail",
  auth: {
    user: process.env.EMAIL_USER,
    pass: process.env.EMAIL_PASSWORD,
  },
});

export const hashPassword = async (password: string, saltRounds = 10): Promise<string> => {
  const salt = await bcrypt.genSalt(saltRounds);
  return await bcrypt.hash(password, salt);
};

export const validatePassword = async (password: string, hashedPassword: string): Promise<boolean> => {
  return await bcrypt.compare(password, hashedPassword);
};

export const generateToken = (userId: string | number, email: string): string => {
  const payload = { id: userId, email };
  return jwt.sign(payload, process.env.SECRET_KEY || "default_secret_key", { expiresIn: "1h" });
};

export const registerUser = async (username: string, email: string, password: string): Promise<User> => {
  const existingUser = await dbService.findUserByEmail(email);
  if (existingUser) {
    throw new Error("User already exists");
  }

  const hashedPassword = await hashPassword(password);
  return await dbService.createUser({ username, email, password: hashedPassword });
};

export const loginUser = async (email: string, password: string): Promise<{ user: User; token: string } | null> => {
  const user = await dbService.findUserByEmail(email);
  if (user && (await validatePassword(password, user.password))) {
    const token = generateToken(user.id, user.email);
    return { user, token };
  }
  return null;
};

export const initiateLoginWithEmail = async (email: string): Promise<{ message: string; success: boolean }> => {
  const user = await dbService.findUserByEmail(email);
  if (user) {
    const otp = crypto.randomInt(100000, 999999).toString();
    OTPs[email] = otp;

    await transporter.sendMail({
      from: process.env.EMAIL_USER,
      to: email,
      subject: "Your OTP for Login",
      text: `Your OTP is: ${otp}`,
    });

    return { message: "OTP sent to your email", success: true };
  }
  return { message: "User not found", success: false };
};

export const validateOTP = async (email: string, otp: string): Promise<{ user: User; token: string } | null> => {
  if (OTPs[email] === otp) {
    delete OTPs[email];
    const user = await dbService.findUserByEmail(email);
    if (user) {
      const token = generateToken(user.id, user.email);
      return { user, token };
    }
  }
  return null;
};