import "server-only";
import { randomBytes, createHash } from "node:crypto";
import { and, eq, gt, isNull, lt, sql } from "drizzle-orm";
import { db } from "@/lib/db/client";
import { passwordResetTokens } from "@/lib/db/schema";

const TOKEN_BYTES = 32;

export function hashResetToken(token: string): string {
  return createHash("sha256").update(token).digest("hex");
}

export async function issueResetToken(
  userId: string,
  opts: { issuedBy?: "self" | "admin"; ttlMinutes?: number } = {},
): Promise<{ token: string; expiresAt: Date }> {
  const raw = randomBytes(TOKEN_BYTES).toString("hex");
  const tokenHash = hashResetToken(raw);
  const ttlMinutes = opts.ttlMinutes ?? 60;
  const expiresAt = new Date(Date.now() + ttlMinutes * 60_000);
  await db.insert(passwordResetTokens).values({
    tokenHash,
    userId,
    expiresAt,
    issuedBy: opts.issuedBy ?? "self",
  });
  return { token: raw, expiresAt };
}

export async function consumeResetToken(raw: string): Promise<string | null> {
  const tokenHash = hashResetToken(raw);
  const now = new Date();
  const [row] = await db
    .select()
    .from(passwordResetTokens)
    .where(
      and(
        eq(passwordResetTokens.tokenHash, tokenHash),
        isNull(passwordResetTokens.usedAt),
        gt(passwordResetTokens.expiresAt, now),
      ),
    )
    .limit(1);
  if (!row) return null;
  await db
    .update(passwordResetTokens)
    .set({ usedAt: now })
    .where(eq(passwordResetTokens.tokenHash, tokenHash));
  return row.userId;
}

export async function invalidateUserResetTokens(userId: string): Promise<void> {
  await db
    .update(passwordResetTokens)
    .set({ usedAt: new Date() })
    .where(
      and(
        eq(passwordResetTokens.userId, userId),
        isNull(passwordResetTokens.usedAt),
      ),
    );
}

export async function cleanupExpiredResetTokens(): Promise<number> {
  const cutoff = new Date(Date.now() - 7 * 24 * 60 * 60_000);
  const result = await db
    .delete(passwordResetTokens)
    .where(lt(passwordResetTokens.expiresAt, cutoff))
    .returning({ tokenHash: passwordResetTokens.tokenHash });
  return result.length;
}

// Peek-only — never reveal the raw token. Useful for admin UI.
export async function resetTokenStatsForUser(userId: string) {
  const [row] = await db
    .select({
      outstanding: sql<number>`count(*)`.mapWith(Number),
    })
    .from(passwordResetTokens)
    .where(
      and(
        eq(passwordResetTokens.userId, userId),
        isNull(passwordResetTokens.usedAt),
        gt(passwordResetTokens.expiresAt, new Date()),
      ),
    );
  return { outstanding: row?.outstanding ?? 0 };
}
