/**
 * CM: admin user creation can defer the password to the new user.
 *
 * Two modes:
 *   - setup-email mode (default): no password is supplied at creation. The
 *     account is provisioned (profile + port role) and a set-password link is
 *     dispatched via better-auth. (The welcome-vs-reset framing of that email
 *     is covered by tests/unit/email/account-setup-email-routing.test.ts.)
 *   - manual mode: the admin supplies a password inline; no email is sent.
 */

import { afterAll, describe, expect, it, vi } from 'vitest';
import { eq } from 'drizzle-orm';

import { db } from '@/lib/db';
import { account, roles, user, userProfiles } from '@/lib/db/schema';
import { auth } from '@/lib/auth';
import { createUser } from '@/lib/services/users.service';
import { makePort, makeAuditMeta } from '../helpers/factories';

describe('createUser - set-password email flow', () => {
  const createdUserIds: string[] = [];

  afterAll(async () => {
    // user_port_roles are port-scoped and cleaned by global teardown; the
    // auth user + profile + account rows are global, so purge them here.
    for (const id of createdUserIds) {
      await db.delete(account).where(eq(account.userId, id));
      await db.delete(userProfiles).where(eq(userProfiles.userId, id));
      await db.delete(user).where(eq(user.id, id));
    }
  });

  async function salesRoleId(): Promise<string> {
    const r = await db.query.roles.findFirst({ where: eq(roles.name, 'sales_manager') });
    if (!r) throw new Error('sales_manager role not seeded — run pnpm db:seed');
    return r.id;
  }

  it('provisions the user without a password and emails a set-password link', async () => {
    const port = await makePort();
    const roleId = await salesRoleId();
    const resetSpy = vi
      .spyOn(auth.api, 'requestPasswordReset')
      .mockResolvedValue({ status: true } as never);

    try {
      const email = `setup-test-${Date.now()}-a@example.test`;
      const result = await createUser(
        port.id,
        {
          email,
          name: 'Jane Doe',
          displayName: 'Jane Doe',
          roleId,
          sendSetupEmail: true,
          residentialAccess: false,
        },
        makeAuditMeta(),
      );
      createdUserIds.push(result.userId);

      // Provisioned with the assigned role, ready to sign in once they set a password.
      expect(result.role.name).toBe('sales_manager');
      // A set-password email was dispatched to their address.
      expect(resetSpy).toHaveBeenCalledTimes(1);
      expect(resetSpy.mock.calls[0]?.[0]?.body?.email).toBe(email);
    } finally {
      resetSpy.mockRestore();
    }
  });

  it('uses the supplied password and sends no email in manual mode', async () => {
    const port = await makePort();
    const roleId = await salesRoleId();
    const resetSpy = vi
      .spyOn(auth.api, 'requestPasswordReset')
      .mockResolvedValue({ status: true } as never);

    try {
      const email = `setup-test-${Date.now()}-b@example.test`;
      const result = await createUser(
        port.id,
        {
          email,
          name: 'John Roe',
          displayName: 'John Roe',
          roleId,
          password: 'manual-secret-1234',
          sendSetupEmail: false,
          residentialAccess: false,
        },
        makeAuditMeta(),
      );
      createdUserIds.push(result.userId);

      expect(resetSpy).not.toHaveBeenCalled();
    } finally {
      resetSpy.mockRestore();
    }
  });
});
