const crypto = require("crypto");
const { promisify } = require("util");
const jwt = require("jsonwebtoken");
const { OAuth2Client } = require("google-auth-library");
const User = require("../models/userModel");
const catchAsync = require("../utils/catchAsync");
const AppError = require("../utils/appError");
const sendEmail = require("../utils/email");
const fs = require("fs");
const path = require("path");

const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);

// ... (Keep your signToken and createSendToken functions exactly as they were) ...
const signToken = (id) => {
  return jwt.sign({ id }, process.env.JWT_SECRET, {
    expiresIn: process.env.JWT_EXPIRES_IN,
  });
};

const createSendToken = (user, statusCode, res) => {
  const token = signToken(user._id);
  const cookieExpiresInDays = Number(process.env.JWT_COOKIE_EXPIRES_IN) || 1;
  const cookieOptions = {
    expires: new Date(Date.now() + cookieExpiresInDays * 24 * 60 * 60 * 1000),
    httpOnly: true,
  };
  if (process.env.NODE_ENV === "production") cookieOptions.secure = true;

  res.cookie("jwt", token, cookieOptions);

  user.password = undefined;
  user.googleId = undefined;
  user.verificationCode = undefined;

  res.status(statusCode).json({
    status: "success",
    token,
    data: { user },
  });
};

const generateOTP = () => {
  return Math.floor(100000 + Math.random() * 900000).toString();
};

// ... (Keep googleLogin and login exactly as they were) ...
exports.googleLogin = catchAsync(async (req, res, next) => {
  // ... your existing code ...
  const { credential } = req.body;
  const ticket = await client.verifyIdToken({
    idToken: credential,
    audience: process.env.GOOGLE_CLIENT_ID,
  });
  const payload = ticket.getPayload();
  const { email, name, sub: googleId, picture } = payload;

  let user = await User.findOne({ email });

  if (user) {
    if (!user.googleId) {
      user.googleId = googleId;
      user.isVerified = true;
      await user.save({ validateBeforeSave: false });
    }
  } else {
    user = await User.create({
      name: name,
      email: email,
      googleId: googleId,
      photo: picture,
      role: "student",
      isVerified: true,
      birthDate: new Date(),
    });
  }
  createSendToken(user, 200, res);
});

exports.login = catchAsync(async (req, res, next) => {
  // ... your existing code ...
  const { email, password } = req.body;
  if (!email || !password) {
    return next(new AppError("Please provide email and password!", 400));
  }
  const user = await User.findOne({ email }).select("+password");
  const isPasswordCorrect = user
    ? await user.correctPassword(password, user.password)
    : false;

  if (!user || !isPasswordCorrect) {
    return next(new AppError("Incorrect email or password", 401));
  }
  if (user.role !== "admin" && user.role !== "super-admin") {
    return next(new AppError("Access denied. Admin only.", 403));
  }
  createSendToken(user, 200, res);
});

// ============================================================
// 🔥 REVISED SIGNUP (No Delay)
// ============================================================
exports.signupStudent = catchAsync(async (req, res, next) => {
  const existingUser = await User.findOne({ email: req.body.email });

  if (existingUser) {
    if (existingUser.isVerified) {
      return next(
        new AppError("Email already registered. Please log in.", 400),
      );
    } else {
      // User exists but wasn't verified. Delete and recreate.
      await User.deleteOne({ _id: existingUser._id });
    }
  }

  let photo;
  if (req.file) {
    photo = req.file.filename;
  } else if (req.body.photo) {
    photo = req.body.photo;
  } else {
    photo =
      req.body.gender === "female" ? "defaultFemale.png" : "defaultMale.png";
  }

  const newUser = await User.create({
    name: req.body.name,
    email: req.body.email,
    password: req.body.password,
    passwordConfirm: req.body.passwordConfirm,
    gender: req.body.gender,
    birthDate: req.body.birthDate,
    photo: photo,
    role: "student",
    isVerified: false,
    // createdAt is set automatically by Schema defaults
  });

  // Generate OTP
  const otp = generateOTP();
  newUser.verificationCode = crypto
    .createHash("sha256")
    .update(otp)
    .digest("hex");
  newUser.verificationCodeExpires = Date.now() + 10 * 60 * 1000;

  await newUser.save({ validateBeforeSave: false });

  // ⚡️ FAST RESPONSE: Send JSON immediately. Do NOT await email.
  res.status(201).json({
    status: "success",
    message: "Signup successful! Check your email.",
    email: newUser.email,
  });

  // 📧 EMAIL: Send in background
  sendVerificationEmail(newUser, otp).catch((err) => {
    console.error("❌ Background Email Error:", err);
    // Note: We can't send an error response here because res.json is already sent.
    // The user will use the "Resend" button if they don't get the email.
  });
});

// ============================================================
// 🔥 NEW: RESEND OTP
// ============================================================
exports.resendOTP = catchAsync(async (req, res, next) => {
  const { email } = req.body;

  // Only find unverified users
  const user = await User.findOne({ email, isVerified: false });

  if (!user) {
    return next(new AppError("User not found or already verified.", 404));
  }

  const otp = generateOTP();
  user.verificationCode = crypto.createHash("sha256").update(otp).digest("hex");
  user.verificationCodeExpires = Date.now() + 10 * 60 * 1000; // 10 mins

  // Reset the createdAt timestamp so the TTL index doesn't delete them while they try again
  user.createdAt = Date.now();

  await user.save({ validateBeforeSave: false });

  // For Resend, we AWAIT because the user clicked the button and is waiting for feedback
  try {
    await sendVerificationEmail(user, otp);
    res.status(200).json({
      status: "success",
      message: "New code sent!",
    });
  } catch (err) {
    return next(new AppError("Could not send email. Try again.", 500));
  }
});

// ============================================================
// 🔥 HELPER: SEND EMAIL (With Your Template)
// ============================================================
async function sendVerificationEmail(user, otp) {
  // 1. Define Path
  const logoPath = path.join(
    __dirname,
    "../storage/storageMedia/logo/impact-logo.png",
  );

  // 2. Check Existence
  const hasLogo = fs.existsSync(logoPath);

  // 3. Your Custom Template
  const html = `
    <div style="font-family: Helvetica, Arial, sans-serif; max-width: 600px; margin: 0 auto; background-color: #ffffff; border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden;">
      <div style="background-color: #1e3a8a; padding: 30px; text-align: center;">
        ${
          hasLogo
            ? '<img src="cid:impact-logo" alt="Impact Logo" style="max-height: 60px; width: auto; margin-bottom: 15px;">'
            : '<h1 style="color: #fff; margin: 0; margin-bottom: 10px;">IMPACT</h1>'
        }
        
        <h1 style="color: #ffffff; margin: 0; font-size: 24px; font-weight: bold; letter-spacing: 1px;">Impact International Education</h1>
      </div>
      
      <div style="padding: 40px 20px; text-align: center;">
        <h2 style="color: #333333; margin-top: 0;">Welcome to Impact!</h2>
        <p style="color: #666666; font-size: 16px; margin-bottom: 30px;">Please use the verification code below to activate your account:</p>
        
        <div style="background-color: #f3f4f6; display: inline-block; padding: 15px 30px; border-radius: 8px;">
          <h1 style="margin: 0; font-size: 36px; letter-spacing: 8px; color: #1e3a8a; font-weight: bold;">${otp}</h1>
        </div>
        
        <p style="color: #999999; font-size: 14px; margin-top: 30px;">This code expires in 10 minutes.</p>
      </div>
      
      <div style="background-color: #f9fafb; padding: 20px; text-align: center; color: #9ca3af; font-size: 12px; border-top: 1px solid #eee;">
        &copy; ${new Date().getFullYear()} Impact International Education. All rights reserved.
        <br>Powered by <a href="https://spacetechs.net/" style="text-decoration: none; color: fuchsia;">SpaceTechs</a>
      </div>
    </div>
  `;

  // 4. Attachments (Only add if file exists)
  const attachments = hasLogo
    ? [
        {
          filename: "impact-logo.png",
          path: logoPath,
          cid: "impact-logo",
        },
      ]
    : [];

  await sendEmail({
    email: user.email,
    subject: "Your Verification Code",
    html,
  });
}

// ... (Keep verifyOTP, createAdmin, loginStudent, etc. exactly as they were) ...
exports.verifyOTP = catchAsync(async (req, res, next) => {
  const { email, otp } = req.body;
  const hashedOTP = crypto.createHash("sha256").update(otp).digest("hex");
  const user = await User.findOne({
    email,
    verificationCode: hashedOTP,
    verificationCodeExpires: { $gt: Date.now() },
  });

  if (!user) {
    return next(new AppError("Invalid or expired code.", 400));
  }

  user.isVerified = true;
  user.verificationCode = undefined;
  user.verificationCodeExpires = undefined;
  await user.save({ validateBeforeSave: false });

  createSendToken(user, 200, res);
});

exports.createAdmin = catchAsync(async (req, res, next) => {
  // ... existing logic ...
  const existingUser = await User.findOne({ email: req.body.email });
  if (existingUser) {
    return next(new AppError("This email is already registered.", 400));
  }
  let photo;
  if (req.file) {
    photo = req.file.filename;
  } else if (req.body.photo) {
    photo = req.body.photo;
  } else {
    photo =
      req.body.gender === "female" ? "defaultFemale.png" : "defaultMale.png";
  }
  const newUser = await User.create({
    name: req.body.name,
    email: req.body.email,
    password: req.body.password,
    passwordConfirm: req.body.passwordConfirm,
    gender: req.body.gender,
    birthDate: req.body.birthDate,
    photo: photo,
    role: req.body.role || "admin",
    isVerified: true,
  });
  newUser.password = undefined;
  res.status(201).json({
    status: "success",
    data: { user: newUser },
  });
});

exports.loginStudent = catchAsync(async (req, res, next) => {
  // ... existing logic ...
  const { email, password } = req.body;
  if (!email || !password) {
    return next(new AppError("Please provide email and password!", 400));
  }
  const user = await User.findOne({ email }).select("+password");

  if (user && !user.isVerified) {
    return next(
      new AppError("Please verify your email before logging in.", 401),
    );
  }
  const isPasswordCorrect = user
    ? await user.correctPassword(password, user.password)
    : false;

  if (!user || !isPasswordCorrect) {
    return next(new AppError("Incorrect email or password", 401));
  }
  if (user.role !== "student") {
    return next(new AppError("Please use the Admin Portal.", 403));
  }
  createSendToken(user, 200, res);
});

exports.protect = catchAsync(async (req, res, next) => {
  // ... existing logic ...
  let token;
  if (
    req.headers.authorization &&
    req.headers.authorization.startsWith("Bearer")
  ) {
    token = req.headers.authorization.split(" ")[1];
  }
  if (!token) {
    return next(new AppError("You are not logged in!", 401));
  }
  const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
  const currentUser = await User.findById(decoded.id);
  if (!currentUser) {
    return next(new AppError("User no longer exists.", 401));
  }
  if (currentUser.changedPasswordAfter(decoded.iat)) {
    return next(new AppError("User changed password! Log in again.", 401));
  }
  req.user = currentUser;
  next();
});

exports.restrictTo = (...roles) => {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      return next(new AppError("You do not have permission.", 403));
    }
    next();
  };
};

exports.forgotPassword = catchAsync(async (req, res, next) => {
  // ... existing logic ...
  const user = await User.findOne({ email: req.body.email });
  if (!user) {
    return next(new AppError("No user with that email.", 404));
  }
  const resetToken = user.createPasswordResetToken();
  await user.save({ validateBeforeSave: false });

  const resetURL = `${req.protocol}://${req.get(
    "host",
  )}/api/users/resetPassword/${resetToken}`;
  const message = `Forgot password? Submit new password to: ${resetURL}`;

  try {
    await sendEmail({
      email: user.email,
      subject: "Password Reset Token (10 min)",
      message,
    });
    res.status(200).json({ status: "success", message: "Token sent!" });
  } catch (err) {
    user.passwordResetToken = undefined;
    user.passwordResetExpires = undefined;
    await user.save({ validateBeforeSave: false });
    return next(new AppError("Error sending email.", 500));
  }
});

exports.resetPassword = catchAsync(async (req, res, next) => {
  // ... existing logic ...
  const hashedToken = crypto
    .createHash("sha256")
    .update(req.params.token)
    .digest("hex");
  const user = await User.findOne({
    passwordResetToken: hashedToken,
    passwordResetExpires: { $gt: Date.now() },
  });
  if (!user) {
    return next(new AppError("Token invalid or expired", 400));
  }
  user.password = req.body.password;
  user.passwordConfirm = req.body.passwordConfirm;
  user.passwordResetToken = undefined;
  user.passwordResetExpires = undefined;
  await user.save();
  createSendToken(user, 200, res);
});

exports.updatePassword = catchAsync(async (req, res, next) => {
  // ... existing logic ...
  const user = await User.findById(req.user.id).select("+password");
  if (!(await user.correctPassword(req.body.passwordCurrent, user.password))) {
    return next(new AppError("Current password wrong.", 401));
  }
  user.password = req.body.password;
  user.passwordConfirm = req.body.passwordConfirm;
  await user.save();
  createSendToken(user, 200, res);
});

exports.logout = (req, res) => {
  res.cookie("jwt", "loggedout", {
    expires: new Date(Date.now() + 10 * 1000),
    httpOnly: true,
  });
  res.status(200).json({ status: "success", message: "Logged out!" });
};
