import type { Request } from 'express';
import type { AuditWriter } from '../audit/audit.service';
import { AdminUsersController } from './admin-users.controller';
import type { AdminUsersReader, AdminUsersPage } from './admin-users-reader.service';
import type { AdminUsersQueryDto } from './users-query.dto';

const PAGE: AdminUsersPage = {
  items: [
    {
      oid: 'user-oid',
      tid: 'tenant-1',
      audience: 'workforce',
      username: 'jane.doe@apf.example',
      displayName: 'Jane Doe',
      firstSeenAt: '2026-05-10T10:00:00.000Z',
      lastSeenAt: '2026-05-14T08:30:00.000Z',
    },
  ],
  total: 137,
  limit: 50,
  offset: 0,
};

function makeReq(user?: { oid: string }): Request {
  return { session: user !== undefined ? { user } : {} } as unknown as Request;
}

function makeFixture() {
  const usersReader = { findUsers: jest.fn().mockResolvedValue(PAGE) };
  const auditWriter = { adminUsersQuery: jest.fn().mockResolvedValue(undefined) };
  const controller = new AdminUsersController(
    usersReader as unknown as AdminUsersReader,
    auditWriter as unknown as AuditWriter,
  );
  return { controller, usersReader, auditWriter };
}

describe('AdminUsersController.list', () => {
  it('returns the page from AdminUsersReader', async () => {
    const { controller } = makeFixture();
    const page = await controller.list(makeReq({ oid: 'admin-oid' }), {} as AdminUsersQueryDto);
    expect(page).toBe(PAGE);
  });

  it('forwards the validated DTO to AdminUsersReader as-is', async () => {
    const { controller, usersReader } = makeFixture();
    const filters: AdminUsersQueryDto = {
      username: 'jane',
      audience: 'workforce',
      lastSeenAtFrom: '2026-05-01T00:00:00.000Z',
      limit: 25,
      offset: 0,
    };
    await controller.list(makeReq({ oid: 'admin-oid' }), filters);
    expect(usersReader.findUsers).toHaveBeenCalledWith(filters);
  });

  it('emits admin.users.query with filters + result count', async () => {
    const { controller, auditWriter } = makeFixture();
    const filters: AdminUsersQueryDto = { username: 'jane', limit: 50 };
    await controller.list(makeReq({ oid: 'admin-oid' }), filters);
    expect(auditWriter.adminUsersQuery).toHaveBeenCalledWith({
      actor: { oid: 'admin-oid' },
      filters: { username: 'jane', limit: 50 },
      resultCount: PAGE.items.length,
    });
  });

  it('captures an empty filter object verbatim', async () => {
    const { controller, auditWriter } = makeFixture();
    await controller.list(makeReq({ oid: 'admin-oid' }), {} as AdminUsersQueryDto);
    expect(auditWriter.adminUsersQuery).toHaveBeenCalledWith(
      expect.objectContaining({ filters: {} }),
    );
  });

  it('still returns the page when no session.user (defensive — guard normally catches)', async () => {
    const { controller, auditWriter } = makeFixture();
    const page = await controller.list(makeReq(), {} as AdminUsersQueryDto);
    expect(page).toBe(PAGE);
    expect(auditWriter.adminUsersQuery).not.toHaveBeenCalled();
  });

  it('propagates audit write failures (blocking per ADR-0013)', async () => {
    const { controller, auditWriter } = makeFixture();
    auditWriter.adminUsersQuery.mockRejectedValueOnce(new Error('audit_writer denied'));
    await expect(
      controller.list(makeReq({ oid: 'admin-oid' }), {} as AdminUsersQueryDto),
    ).rejects.toThrow('audit_writer denied');
  });
});
