/**
 * Unit tests for `sendPasswordResetEmail` in `src/actions/admin/users.ts`.
 *
 * The action must:
 *   1. Reject unauthenticated callers
 *   2. Reject non-platform-admin callers
 *   3. Trim + lowercase the email before calling Neon Auth
 *   4. Call `requestPasswordReset` with the correct `redirectTo`
 *   5. Return success on a clean Neon Auth response
 *   6. Propagate Neon Auth errors back to the UI
 */
import { describe, it, expect, vi, beforeEach } from "vitest";

const {
  mockGetAdminUser,
  mockRequestPasswordReset,
} = vi.hoisted(() => ({
  mockGetAdminUser: vi.fn(),
  mockRequestPasswordReset: vi.fn(),
}));

vi.mock("server-only", () => ({}));

vi.mock("@/lib/admin-permissions", () => ({
  getAdminUser: mockGetAdminUser,
}));

vi.mock("@/lib/auth", () => ({
  requestPasswordReset: mockRequestPasswordReset,
}));

vi.mock("@/lib/db", () => ({
  query: vi.fn(),
  withTx: vi.fn(),
}));

vi.mock("next/headers", () => ({
  cookies: () => Promise.resolve({ get: () => undefined }),
}));

import { sendPasswordResetEmail } from "@/actions/admin/users";

const PLATFORM_ADMIN = {
  id: "platform-1",
  user_id: "platform-1",
  email: "root@example.com",
  display_name: "Root",
  brand_id: null,
  brand_ids: [],
  brand_slug: null,
  role: "platform_admin" as const,
  active: true,
  auth_provider: "neon_auth" as const,
  can_manage_products: true,
  can_manage_stops: true,
  can_manage_orders: true,
  can_manage_pickup: true,
  can_manage_messages: true,
  can_manage_refunds: true,
  can_manage_users: true,
  can_manage_water_log: true,
  can_manage_reports: true,
  can_manage_settings: true,
  can_manage_billing: true,
  can_manage_branding: true,
  can_manage_marketing: true,
  can_manage_team: true,
  must_change_password: false,
};

const BRAND_ADMIN = { ...PLATFORM_ADMIN, role: "brand_admin" as const, brand_id: "brand-tux" };

beforeEach(() => {
  vi.clearAllMocks();
  process.env.NEXT_PUBLIC_SITE_URL = "http://localhost:4000";
  mockGetAdminUser.mockResolvedValue(PLATFORM_ADMIN);
  mockRequestPasswordReset.mockResolvedValue({ data: null, error: null });
});

describe("sendPasswordResetEmail — authorization", () => {
  it("rejects unauthenticated callers", async () => {
    mockGetAdminUser.mockResolvedValue(null);
    const r = await sendPasswordResetEmail("user@example.com");
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/not authenticated/i);
    expect(mockRequestPasswordReset).not.toHaveBeenCalled();
  });

  it("rejects non-platform-admin callers", async () => {
    mockGetAdminUser.mockResolvedValue(BRAND_ADMIN);
    const r = await sendPasswordResetEmail("user@example.com");
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/only platform admins/i);
    expect(mockRequestPasswordReset).not.toHaveBeenCalled();
  });
});

describe("sendPasswordResetEmail — input handling", () => {
  it("rejects an empty email without calling Neon Auth", async () => {
    const r = await sendPasswordResetEmail("   ");
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/required/i);
    expect(mockRequestPasswordReset).not.toHaveBeenCalled();
  });

  it("trims and lowercases the email before calling Neon Auth", async () => {
    await sendPasswordResetEmail("  User@Example.COM  ");
    expect(mockRequestPasswordReset).toHaveBeenCalledWith({
      email: "user@example.com",
      redirectTo: "http://localhost:4000/reset-password",
    });
  });
});

describe("sendPasswordResetEmail — happy path", () => {
  it("returns success when Neon Auth accepts the request", async () => {
    mockRequestPasswordReset.mockResolvedValue({ data: { ok: true }, error: null });
    const r = await sendPasswordResetEmail("user@example.com");
    expect(r.success).toBe(true);
    expect(r.error).toBeNull();
  });
});

describe("sendPasswordResetEmail — error propagation", () => {
  it("returns a clear error when Neon Auth returns a structured error", async () => {
    mockRequestPasswordReset.mockResolvedValue({
      data: null,
      error: { code: "USER_NOT_FOUND", message: "no such user" },
    });
    const r = await sendPasswordResetEmail("user@example.com");
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/no such user/);
  });

  it("falls back to the error code if the message is missing", async () => {
    mockRequestPasswordReset.mockResolvedValue({
      data: null,
      error: { code: "RATE_LIMITED" },
    });
    const r = await sendPasswordResetEmail("user@example.com");
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/RATE_LIMITED/);
  });

  it("catches thrown exceptions and surfaces them as a string", async () => {
    mockRequestPasswordReset.mockRejectedValue(new Error("network down"));
    const r = await sendPasswordResetEmail("user@example.com");
    expect(r.success).toBe(false);
    expect(r.error).toMatch(/network down/);
  });
});
