import { beforeEach, expect, mock, test } from 'bun:test';

import type { InviteComplexUserResponse } from '@repo/api-contract';

const inviteComplexUserMock = mock(async () => undefined as unknown as InviteComplexUserResponse);

class MockComplexMembersError extends Error {
  status: 400 | 403 | 404 | 409;

  constructor(message: string, status: 400 | 403 | 404 | 409 = 400) {
    super(message);
    this.name = 'ComplexMembersError';
    this.status = status;
  }
}

const { createInviteComplexUserHandler } = await import(
  '@/modules/complex/handlers/invite-complex-user.handler'
);

const inviteComplexUserHandler = createInviteComplexUserHandler({
  inviteComplexUser: inviteComplexUserMock,
  ComplexMembersError: MockComplexMembersError,
});

type HandlerContext = {
  get: (key: 'user' | 'session') => { id: string } | undefined;
  req: {
    param: () => Record<string, string>;
    valid: () => unknown;
  };
  json: ReturnType<typeof mock>;
};

function createContext(input: {
  userId: string;
  params: Record<string, string>;
  body: unknown;
}) {
  const json = mock((payload: unknown, init?: unknown) => ({
    payload,
    init,
  }));

  const c = {
    get: (key: 'user' | 'session') => {
      if (key === 'user') {
        return { id: input.userId };
      }

      return undefined;
    },
    req: {
      param: () => input.params,
      valid: () => input.body,
    },
    json,
  } as unknown as HandlerContext;

  return { c, json };
}

beforeEach(() => {
  inviteComplexUserMock.mockReset();
});

test('returns 201 with the service result', async () => {
  const serviceResult = {
    invitation: {
      id: 'inv-1',
      complexId: 'complex-1',
      email: 'new.member@example.com',
      status: 'PENDING',
      createdAt: '2026-04-22T00:00:00.000Z',
      expiresAt: '2026-04-29T00:00:00.000Z',
      acceptedAt: null,
      revokedAt: null,
    },
  } as const;

  inviteComplexUserMock.mockResolvedValue(serviceResult);

  const { c, json } = createContext({
    userId: 'admin-1',
    params: { id: '11111111-1111-4111-8111-111111111111' },
    body: { email: 'new.member@example.com' },
  });

  const response = await inviteComplexUserHandler(c as never);

  expect(inviteComplexUserMock).toHaveBeenCalledWith(
    'admin-1',
    '11111111-1111-4111-8111-111111111111',
    { email: 'new.member@example.com' }
  );
  expect(json.mock.calls[0]?.[0]).toEqual(serviceResult);
  expect(json.mock.calls[0]?.[1]).toBe(201);
  expect(response).toBeDefined();
});

test('maps ComplexMembersError to an error response', async () => {
  inviteComplexUserMock.mockRejectedValue(
    new MockComplexMembersError('Solo un ADMIN puede administrar usuarios.', 403)
  );

  const { c, json } = createContext({
    userId: 'employee-1',
    params: { id: '11111111-1111-4111-8111-111111111111' },
    body: { email: 'new.member@example.com' },
  });

  const response = await inviteComplexUserHandler(c as never);

  expect(inviteComplexUserMock).toHaveBeenCalledWith(
    'employee-1',
    '11111111-1111-4111-8111-111111111111',
    { email: 'new.member@example.com' }
  );
  expect(json.mock.calls[0]?.[0]).toEqual({
    message: 'Solo un ADMIN puede administrar usuarios.',
  });
  expect(json.mock.calls[0]?.[1]).toEqual({ status: 403 });
  expect(response).toBeDefined();
});
