const CustomersModel = require("../models/Customers");
const ForgotPassword = require("../models/ForgotPassword");
const passport = require("../util/passport-config");
const jwt = require("jsonwebtoken");
const errorHandler = require("../util/errorHandler");
const crypto = require("crypto");
const nodemailer = require("nodemailer");
const bcryptjs = require("bcryptjs");
const Customers = require("../models/Customers");
const Rpaf = require("../models/Rpaf");
const emailBuilder = require("../helpers/transporter");
const {
  verificationEmailRegister,
  passwordResetEmail,
} = require("../helpers/email-templates/html-bodys");
const {
  accountVerifiedTemplate,
  verifyEmailTemplate,
} = require("../helpers/email-templates/html-bodys-new");
const {
  generateRandomString,
  generateSecureRandomSixDigit,
} = require("../helpers/common");
const QrLoginSession = require("../models/QrLoginRecords");

exports.accountLogin = (req, res, next) => {
  const { rememberMe = false } = req.body;
  passport.authenticate("login", (err, customer, info) => {
    if (err) {
      return next(err);
    }

    if (!customer) {
      return res.status(400).json({ success: false, message: info.message });
    }

    Rpaf.findOne({
      where: {
        customer_id: customer.id,
      },
      attributes: ["id", "status"],
    }).then((rpafDetail) => {
      const tokenExpiration = rememberMe ? "7d" : "2h";
      const token = jwt.sign(
        { customerId: customer.id, impersonation: false },
        process.env.SECRETKEY,
        {
          expiresIn: tokenExpiration,
        }
      );

      res.json({
        success: true,
        account_type: customer.type,
        token,
        rpafDetail,
      });
    });
  })(req, res, next);
};

exports.accountCreate = (req, res, next) => {
  passport.authenticate("signup", { session: false }, (err, customer, info) => {
    if (err) {
      return next(err);
    }

    if (!customer) {
      return res.status(400).json({ success: false, message: info.message });
    }

    const token = jwt.sign(
      { customerId: customer.id, impersonation: false },
      process.env.SECRETKEY,
      {
        expiresIn: "2h",
      }
    );

    res.json({ success: true, token });
  })(req, res, next);
};

exports.verifyEmail = async (req, res, next) => {
  try {
    const token = req.params.token;

    const user = await Customers.findOne({
      where: { verificationToken: token, status: "For Approval" },
    });

    if (!user) {
      const errorMessage = "Invalid or already used verification token";
      return res.status(400).send(`
        <!DOCTYPE html>
        <html lang="en">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Error</title>
          <style>
            body { font-family: Arial, sans-serif; }
            .container { text-align: center; margin-top: 50px; }
            .logo { width: 200px; height: auto; margin: 0 auto 20px; display: block; }
            .error-message { color: red; font-size: 24px; }
          </style>
        </head>
        <body>
          <div class="container">
            <img src="https://buildhub.ph/img/buildhub.png" alt="BuildHub Logo" class="logo">
            <p class="error-message">${errorMessage}</p>
          </div>
        </body>
        </html>
      `);
    }

    const tokenTimestamp = new Date(user.verificationTokenTimestamp);
    const now = new Date(Date.now() + 8 * 60 * 60 * 1000);
    const fiveMinutesAgo = new Date(now - 5 * 60 * 1000);

    if (tokenTimestamp < fiveMinutesAgo) {
      const newToken = generateRandomToken();
      await user.update({
        verificationToken: newToken,
        verificationTokenTimestamp: now,
      });
      await sendVerificationEmail(
        user.email,
        `${user.first_name} ${user.last_name}`,
        newToken
      );
      const message = "Token expired. New token has been sent to your email.";
      return res.status(400).send(`
        <!DOCTYPE html>
        <html lang="en">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Error</title>
          <style>
            body { font-family: Arial, sans-serif; }
            .container { text-align: center; margin-top: 50px; }
            .logo { width: 200px; height: auto; margin: 0 auto 20px; display: block; }
            .info-message { color: blue; font-size: 24px; }
          </style>
        </head>
        <body>
          <div class="container">
            <img src="https://buildhub.ph/img/buildhub.png" alt="BuildHub Logo" class="logo">
            <p class="info-message">${message}</p>
          </div>
        </body>
        </html>
      `);
    }

    // Update user's status to 'Approved' and mark token as used
    await Customers.update(
      { status: "Verified", verificationTokenUsed: true },
      { where: { verificationToken: token } }
    );

    // Respond with a success message
    const successMessage =
      "Email verified successfully. Your account has been approved.";
    return res
      .status(200)
      .send(accountVerifiedTemplate(`${user.first_name} ${user.last_name}`));
  } catch (error) {
    // If an error occurs, respond with a server error message
    console.error("Error verifying email:", error);
    const errorMessage = "An error occurred while verifying email";
    return res.status(500).send(`
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Error</title>
        <style>
          body { font-family: Arial, sans-serif; }
          .container { text-align: center; margin-top: 50px; }
          .logo { width: 200px; height: auto; margin: 0 auto 20px; display: block; }
          .error-message { color: red; font-size: 24px; }
        </style>
      </head>
      <body>
        <div class="container">
          <img src="https://buildhub.ph/img/buildhub.png" alt="BuildHub Logo" class="logo">
          <p class="error-message">${errorMessage}</p>
        </div>
      </body>
      </html>
    `);
  }
};

// Function to generate a random 6-digit token
const generateRandomToken = () => {
  return Math.floor(100000 + Math.random() * 900000).toString();
};

// Function to send verification email with the new token
async function sendVerificationEmail(email, name, token) {
  try {
    const receivers = [email];

    const info = await emailBuilder(
      receivers,
      [],
      "Verify Your Email Address",
      true,
      verifyEmailTemplate(name, token),
      []
    );
  } catch (error) {
    console.error("Error sending email:", error);
  }
}

exports.forgotPassword = async (req, res, next) => {
  try {
    const email = req.body.email;

    const customer = await CustomersModel.findOne({ where: { email } });

    if (customer) {
      const token = `${generateRandomString()}${generateSecureRandomSixDigit()}`;
      const singaporeTime = new Date().toLocaleString("en-US", {
        timeZone: "Asia/Singapore",
      });

      await ForgotPassword.create({
        customer_id: customer.id,
        token: token,
        // created_at: new Date().toISOString(),
        created_at: singaporeTime,
      });

      await sendResetPasswordEmail(email, token);

      // Return the token in the response
      res.status(200).json({ message: "Token generated successfully" });
    } else {
      res.status(404).json({ error: "Email not found" });
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
};

async function sendResetPasswordEmail(email, token) {
  try {
    const receivers = [email];
    const info = await emailBuilder(
      receivers,
      [],
      "Reset your password",
      true,
      passwordResetEmail(token),
      []
    );
    console.log("Email sent: " + info.response);
  } catch (error) {
    console.error("Error sending email:", error);
  }
}

exports.resetPassword = async (req, res, next) => {
  try {
    const token = req.params.token;
    const newPassword = req.body.newPassword;
    const confirmPassword = req.body.confirmPassword;

    const forgotPasswordEntry = await ForgotPassword.findOne({
      where: { token },
    });

    if (forgotPasswordEntry) {
      const tokenCreationTime = new Date(forgotPasswordEntry.created_at);
      const currentTime = new Date();

      if (currentTime - tokenCreationTime <= 15 * 60 * 1000) {
        const customer = await CustomersModel.findByPk(
          forgotPasswordEntry.customer_id
        );

        if (customer) {
          if (newPassword === confirmPassword) {
            const Password = await newPassword;

            customer.password = Password;
            await customer.save();
            await forgotPasswordEntry.destroy();

            res
              .status(200)
              .json({ success: true, message: "Password reset successfully" });
          } else {
            res.status(200).json({
              success: false,
              message: "New password and confirm password do not matched.",
              error: "New password and confirm password do not match",
            });
          }
        } else {
          res.status(200).json({
            success: false,
            message: "Account not found.",
            error: "Customer not found",
          });
        }
      } else {
        res.status(200).json({
          success: false,
          message: "Forgot password token expired. ",
          error: "Token expired",
        });
      }
    } else {
      res.status(200).json({
        success: false,
        message:
          "Token is already used or not valid, please try requesting another.",
        error: "Invalid token",
      });
    }
  } catch (error) {
    res.status(500).json({
      success: false,
      message: "Internal Server Error. ",
      error: error.message,
    });
  }
};

exports.updateCredentials = async (req, res, next) => {
  try {
    const { customerId } = req;
    const { oldPassword, newPassword } = req.body;

    const customer = await Customers.findOne({
      where: {
        id: customerId,
      },
    });

    if (!customer) {
      const message = {
        success: false,
        message: "User not found",
      };
      res.status(404).json(message);
      return;
    }
    const currentPassword = customer.password;

    bcryptjs.compare(oldPassword, currentPassword, async (err, result) => {
      if (err) {
        console.error(err);
        res.sendStatu(500);
        return;
      }

      if (result) {
        customer.password = newPassword;

        await customer.save();

        res.status(200).json({
          success: true,
          message: "Password successfully changed",
        });
      } else {
        res.status(401).json({
          success: false,
          message: "Password not matched",
        });
      }
    });
  } catch (error) {
    console.error(error);
  }
};

// QR Code Login
exports.generateQrRecord = async (req, res, next) => {
  try {
    const ipAddress = req.ip || req.connection.remoteAddress;
    const userAgent = req.headers["user-agent"] || "";

    const qrToken = crypto.randomBytes(32).toString("hex");

    const expiresAt = new Date(Date.now() + 2 * 60 * 1000);

    const record = await QrLoginSession.create({
      qr_token: qrToken,
      status: "pending",
      ip_address: ipAddress,
      user_agent: userAgent,
      created_at: new Date(),
      expires_at: expiresAt,
    });

    res.status(200).json({
      success: true,
      qrToken: record.qr_token,
      expiresAt: record.expires_at,
    });
  } catch (error) {
    console.error("Error generating QR record:", error);
    res
      .status(500)
      .json({ success: false, message: "Failed to generate QR code session." });
  }
};

exports.checkQrRecordStatus = async (req, res, next) => {
  try {
    const { qrToken } = req.query;

    if (!qrToken) {
      return res
        .status(400)
        .json({ success: false, message: "QR token is required." });
    }

    const session = await QrLoginSession.findOne({
      where: { qr_token: qrToken },
    });

    if (!session) {
      return res
        .status(200)
        .json({ success: false, message: "QR session not found." });
    }

    const now = new Date();
    if (now > session.expires_at) {
      return res
        .status(200)
        .json({ success: false, message: "QR session has expired." });
    }

    if (session.status === "authenticated" && session.user_id) {
      const customerId = session.user_id;

      const token = jwt.sign(
        { customerId: customerId, impersonation: false },
        process.env.SECRETKEY,
        {
          expiresIn: "2h",
        }
      );

      const customerDetailPromise = Customers.findByPk(customerId);

      const buildCreditPromise = Rpaf.findOne({
        where: {
          customer_id: customerId,
        },
        attributes: ["id", "status"],
      });

      const [customerDetail, buildCredit] = await Promise.all([
        customerDetailPromise,
        buildCreditPromise,
      ]);

      return res.status(200).json({
        success: true,
        authenticated: true,
        token,
        scannedAt: session.scanned_at,
        authenticatedAt: session.authenticated_at,
        account_type: customerDetail.type,
        buildCredit,
      });
    }

    return res.status(200).json({
      success: true,
      authenticated: false,
      status: session.status,
      scannedAt: session.scanned_at,
      authenticatedAt: session.authenticated_at,
    });
  } catch (error) {
    console.error("Error checking QR status:", error);
    res
      .status(500)
      .json({ success: false, message: "Failed to check QR code status." });
  }
};

exports.updateQrRecordAsAuthenticated = async (req, res, next) => {
  try {
    const { customerId } = req;
    const { qrToken } = req.body;

    if (!qrToken || !customerId) {
      return res.status(400).json({
        success: false,
        message: "QR token and customer ID are required.",
      });
    }

    const session = await QrLoginSession.findOne({
      where: { qr_token: qrToken },
    });

    if (!session) {
      return res.status(404).json({
        success: false,
        message: "QR session not found.",
      });
    }

    const now = new Date();

    if (now > session.expires_at) {
      return res.status(410).json({
        success: false,
        message: "QR session has expired.",
      });
    }

    if (session.status === "authenticated") {
      return res.status(400).json({
        success: false,
        message: "QR session is already authenticated.",
      });
    }

    session.status = "authenticated";
    session.authenticated_at = now;
    session.user_id = customerId;

    await session.save();

    return res.status(200).json({
      success: true,
      message: "QR session updated as authenticated.",
    });
  } catch (error) {
    console.error("Error updating QR session:", error);
    res.status(500).json({
      success: false,
      message: "Failed to update QR session.",
    });
  }
};
