import jwt from 'jsonwebtoken';
import crypto from 'crypto';
import { Admin } from '../models/Admin.js';
import { ApiError } from '../utils/ApiError.js';
import { asyncHandler } from '../utils/asyncHandler.js';
import { sendPasswordResetEmail } from '../services/emailService.js';

const RESET_TOKEN_TTL_MS = 60 * 60 * 1000; // 1 hour
const GENERIC_RESET_MESSAGE = "If that email is registered, we've sent a password reset link.";

function hashToken(token) {
  return crypto.createHash('sha256').update(token).digest('hex');
}

function signToken(admin) {
  return jwt.sign(
    { sub: admin.id, email: admin.email, name: admin.name },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
  );
}

export const login = asyncHandler(async (req, res) => {
  const { email, password } = req.body;

  if (!email || !password) {
    throw new ApiError(400, 'Email and password are required');
  }

  const admin = await Admin.findByEmail(email.toLowerCase().trim());
  if (!admin) {
    throw new ApiError(401, 'Incorrect email or password');
  }

  const valid = await Admin.verifyPassword(admin, password);
  if (!valid) {
    throw new ApiError(401, 'Incorrect email or password');
  }

  const token = signToken(admin);

  res.json({
    success: true,
    token,
    admin: { id: admin.id, name: admin.name, email: admin.email },
  });
});

export const me = asyncHandler(async (req, res) => {
  const admin = await Admin.findById(req.admin.id);
  if (!admin) throw new ApiError(404, 'Admin not found');
  res.json({ success: true, admin });
});

// POST /api/auth/forgot-password — always responds with the same generic
// message whether or not the email matches an account, so this endpoint
// can't be used to enumerate registered admin emails.
export const forgotPassword = asyncHandler(async (req, res) => {
  const { email } = req.body;
  if (!email) throw new ApiError(400, 'Email is required');

  const admin = await Admin.findByEmail(email.toLowerCase().trim());

  if (admin) {
    const token = crypto.randomBytes(32).toString('hex');
    await Admin.setResetToken(admin.id, {
      tokenHash: hashToken(token),
      expiresAt: new Date(Date.now() + RESET_TOKEN_TTL_MS),
    });

    const resetUrl = `${process.env.CLIENT_URL}/admin/reset-password?token=${token}`;
    await sendPasswordResetEmail(admin.email, resetUrl);
  }

  res.json({ success: true, message: GENERIC_RESET_MESSAGE });
});

// POST /api/auth/reset-password
export const resetPassword = asyncHandler(async (req, res) => {
  const { token, password } = req.body;
  if (!token || !password) throw new ApiError(400, 'Token and new password are required');
  if (password.length < 8) throw new ApiError(400, 'Password must be at least 8 characters');

  const admin = await Admin.findByResetTokenHash(hashToken(token));
  if (!admin) throw new ApiError(400, 'This reset link is invalid or has expired');

  await Admin.updatePassword(admin.id, password);

  res.json({ success: true, message: 'Password updated — you can now sign in.' });
});

// PUT /api/auth/profile — update the signed-in admin's own name/email
export const updateProfile = asyncHandler(async (req, res) => {
  const { name, email } = req.body;
  if (!name || !email) throw new ApiError(400, 'Name and email are required');

  const normalizedEmail = email.toLowerCase().trim();
  const existing = await Admin.findByEmail(normalizedEmail);
  if (existing && existing.id !== req.admin.id) {
    throw new ApiError(409, 'That email is already in use by another admin');
  }

  const admin = await Admin.updateProfile(req.admin.id, { name: name.trim(), email: normalizedEmail });
  res.json({ success: true, admin });
});

// POST /api/auth/change-password — for a signed-in admin changing their
// own password (distinct from the emailed forgot/reset-password flow).
export const changePassword = asyncHandler(async (req, res) => {
  const { currentPassword, newPassword } = req.body;
  if (!currentPassword || !newPassword) {
    throw new ApiError(400, 'Current and new password are required');
  }
  if (newPassword.length < 8) throw new ApiError(400, 'Password must be at least 8 characters');

  const admin = await Admin.findByEmail(req.admin.email);
  const valid = await Admin.verifyPassword(admin, currentPassword);
  // 400, not 401 — the frontend's axios interceptor treats any 401 as an
  // invalid session and force-logs-out, which would wrongly kick a signed-in
  // admin back to the login page just for mistyping their current password.
  if (!valid) throw new ApiError(400, 'Current password is incorrect');

  await Admin.updatePassword(admin.id, newPassword);

  res.json({ success: true, message: 'Password updated.' });
});
