"use server";

import { revalidatePath } from "next/cache";
import { eq, sql } from "drizzle-orm";
import { db } from "@/lib/db/client";
import { users } from "@/lib/db/schema";
import { requireUser } from "@/lib/auth/admin-guard";
import {
  assessPasswordStrength,
  hashPassword,
  verifyPassword,
} from "@/lib/auth/password";
import { invalidateUserResetTokens } from "@/lib/auth/reset-token";
import { sendTemplatedEmail } from "@/lib/email/send";
import { signOut } from "@/lib/auth/auth";
import { logAdminAction } from "@/lib/audit";

export type ChangePasswordState = {
  ok: boolean;
  message?: string;
  fieldErrors?: Record<string, string[]>;
};

export async function changePassword(
  _prev: ChangePasswordState,
  formData: FormData,
): Promise<ChangePasswordState> {
  const session = await requireUser();
  const userId = (session.user as { id?: string }).id;
  if (!userId) return { ok: false, message: "Session expired." };

  const current = String(formData.get("current") ?? "");
  const next = String(formData.get("next") ?? "");
  const confirm = String(formData.get("confirm") ?? "");

  if (!current) {
    return { ok: false, fieldErrors: { current: ["Enter your current password."] } };
  }
  if (next !== confirm) {
    return { ok: false, fieldErrors: { confirm: ["Passwords do not match."] } };
  }
  const issues = assessPasswordStrength(next);
  if (issues.length > 0) {
    return { ok: false, fieldErrors: { next: issues } };
  }
  if (next === current) {
    return {
      ok: false,
      fieldErrors: { next: ["New password must be different from the current one."] },
    };
  }

  const [row] = await db
    .select({
      id: users.id,
      email: users.email,
      name: users.name,
      passwordHash: users.passwordHash,
    })
    .from(users)
    .where(eq(users.id, userId))
    .limit(1);
  if (!row || !row.passwordHash) {
    return { ok: false, message: "Account not found." };
  }

  const verified = await verifyPassword(current, row.passwordHash);
  if (!verified) {
    return {
      ok: false,
      fieldErrors: { current: ["Current password is incorrect."] },
    };
  }

  const passwordHash = await hashPassword(next);
  // Bump token_version so every other open session is immediately invalidated.
  await db
    .update(users)
    .set({
      passwordHash,
      tokenVersion: sql`${users.tokenVersion} + 1`,
    })
    .where(eq(users.id, userId));
  await invalidateUserResetTokens(userId);

  try {
    await sendTemplatedEmail({
      to: row.email,
      templateSlug: "password-changed",
      variables: {
        name: row.name,
        changedAt: new Date().toUTCString(),
      },
    });
  } catch (err) {
    console.error("[change-password] confirmation email failed", err);
  }

  revalidatePath("/account/security");
  return { ok: true, message: "Password updated. Your other sessions will be signed out on next request." };
}

export type SignOutAllState = { ok: boolean; message?: string };

export async function signOutAllDevices(): Promise<void> {
  const session = await requireUser();
  const userId = (session.user as { id?: string }).id;
  if (!userId) return;
  await db
    .update(users)
    .set({ tokenVersion: sql`${users.tokenVersion} + 1` })
    .where(eq(users.id, userId));
  await logAdminAction({
    adminUserId: userId,
    action: "session_signed_out_all",
    targetId: userId,
  });
  await signOut({ redirectTo: "/login?reset=devices" });
}
