import { randomBytes } from "node:crypto";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { createTRPCRouter, publicProcedure } from "../trpc.js";
import { getAppBaseUrl } from "../lib/app-base-url.js";
import { sendEmail } from "../lib/email.js";

const RESET_TTL_MS = 60 * 60 * 1000; // 1 hour

function resetEmailHtml(resetUrl: string): string {
  return `
    <p>You requested a password reset for your CapaKraken account.</p>
    <p>Click the link below to set a new password:</p>
    <p><a href="${resetUrl}">${resetUrl}</a></p>
    <p>This link expires in 1 hour and can only be used once.</p>
    <p>If you did not request this, you can ignore this email.</p>
  `;
}

export const authRouter = createTRPCRouter({
  /**
   * Request a password reset email.
   * Always returns { success: true } — even if the email is not registered —
   * to prevent user enumeration.
   */
  requestPasswordReset: publicProcedure
    .input(z.object({ email: z.string().email() }))
    .mutation(async ({ ctx, input }) => {
      const user = await ctx.db.user.findUnique({
        where: { email: input.email },
        select: { id: true, email: true },
      });

      if (!user) {
        // Timing-safe: don't reveal whether the email exists
        return { success: true };
      }

      // Delete any existing (unused) reset tokens for this email
      await ctx.db.passwordResetToken.deleteMany({
        where: { email: input.email, usedAt: null },
      });

      const token = randomBytes(32).toString("hex");
      const expiresAt = new Date(Date.now() + RESET_TTL_MS);

      await ctx.db.passwordResetToken.create({
        data: { email: input.email, token, expiresAt },
      });

      const resetUrl = `${getAppBaseUrl()}/auth/reset-password/${token}`;

      void sendEmail({
        to: input.email,
        subject: "CapaKraken — reset your password",
        text: `You requested a password reset.\n\nReset your password: ${resetUrl}\n\nThis link expires in 1 hour. If you did not request this, ignore this email.`,
        html: resetEmailHtml(resetUrl),
      });

      return { success: true };
    }),

  /** Validate a reset token and set a new password. */
  resetPassword: publicProcedure
    .input(
      z.object({
        token: z.string().min(1),
        password: z.string().min(8, "Password must be at least 8 characters."),
      }),
    )
    .mutation(async ({ ctx, input }) => {
      const record = await ctx.db.passwordResetToken.findUnique({
        where: { token: input.token },
      });

      if (!record) {
        throw new TRPCError({ code: "NOT_FOUND", message: "Reset link not found." });
      }
      if (record.usedAt) {
        throw new TRPCError({ code: "BAD_REQUEST", message: "This reset link has already been used." });
      }
      if (record.expiresAt < new Date()) {
        throw new TRPCError({ code: "BAD_REQUEST", message: "This reset link has expired." });
      }

      const { hash } = await import("@node-rs/argon2");
      const passwordHash = await hash(input.password);

      await ctx.db.user.update({
        where: { email: record.email },
        data: { passwordHash },
      });

      await ctx.db.passwordResetToken.update({
        where: { token: input.token },
        data: { usedAt: new Date() },
      });

      return { success: true };
    }),
});
