import crypto from 'crypto';
import { prisma } from '../config/database';
import { logger } from '../utils/logger';

/** Hash a token with SHA-256 for storage (raw token is sent to user, hash is stored in DB). */
function hashToken(token: string): string {
  return crypto.createHash('sha256').update(token).digest('hex');
}

export const passwordResetTokenService = {
  async createToken(userId: string): Promise<string> {
    // Delete any existing tokens for this user
    await prisma.passwordResetToken.deleteMany({ where: { userId } });

    const rawToken = crypto.randomBytes(32).toString('hex');
    const tokenHash = hashToken(rawToken);
    const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour

    await prisma.passwordResetToken.create({
      data: { userId, token: tokenHash, expiresAt },
    });

    // 2026-04-12: userId removed from log to prevent correlation of reset
    // activity with specific accounts via log scraping.
    logger.info('Password reset token created');
    return rawToken; // Send raw token in email; DB stores only the hash
  },

  async validateToken(token: string): Promise<{ valid: boolean; userId?: string; error?: string }> {
    const tokenHash = hashToken(token);
    const record = await prisma.passwordResetToken.findUnique({ where: { token: tokenHash } });

    // Use a generic error message for all failure cases to prevent token state enumeration
    const genericError = 'Invalid or expired reset token';

    if (!record) {
      return { valid: false, error: genericError };
    }

    if (record.expiresAt < new Date()) {
      await prisma.passwordResetToken.delete({ where: { id: record.id } });
      return { valid: false, error: genericError };
    }

    if (record.usedAt) {
      return { valid: false, error: genericError };
    }

    return { valid: true, userId: record.userId };
  },

  async markTokenUsed(token: string): Promise<void> {
    const tokenHash = hashToken(token);
    await prisma.passwordResetToken.update({
      where: { token: tokenHash },
      data: { usedAt: new Date() },
    });
  },

  async cleanupExpiredTokens(): Promise<number> {
    const result = await prisma.passwordResetToken.deleteMany({
      where: { expiresAt: { lt: new Date() } },
    });

    if (result.count > 0) {
      logger.info(`Cleaned up ${result.count} expired password reset tokens`);
    }

    return result.count;
  },
};
