import { GraphQLError } from 'graphql';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { invitationsResolvers } from '../resolvers/invitations.resolver.js';
import { AuthContextProvider } from '../providers/auth-context.provider.js';
import { Auth0ManagementProvider } from '../providers/auth0-management.provider.js';
import { InvitationsProvider } from '../providers/invitations.provider.js';
const createInvitation = invitationsResolvers.Mutation?.createInvitation;
const mockInfo = {};
describe('createInvitation resolver', () => {
    let mockAuthProvider;
    let mockAuth0ManagementProvider;
    let mockInvitationsProvider;
    beforeEach(() => {
        mockAuthProvider = {
            getAuthContext: vi.fn().mockResolvedValue({
                authType: 'jwt',
                user: {
                    userId: 'user-123',
                    roleId: 'business_owner',
                    email: 'owner@example.com',
                    permissions: [],
                    emailVerified: true,
                    permissionsVersion: 1,
                    auth0UserId: 'auth0|owner-123',
                },
                tenant: {
                    businessId: 'business-123',
                    roleId: 'business_owner',
                },
            }),
        };
        mockAuth0ManagementProvider = {
            createBlockedUser: vi.fn().mockResolvedValue('auth0|new-user-123'),
            deleteUser: vi.fn().mockResolvedValue(undefined),
        };
        mockInvitationsProvider = {
            getInvitationByEmailLoader: {
                load: vi.fn().mockResolvedValue([]),
            },
            insertInvitation: vi.fn().mockResolvedValue({
                id: 'inv-123',
                email: 'new.user@example.com',
                roleId: 'employee',
                expiresAt: new Date('2030-01-01T00:00:00.000Z'),
                token: 'invitation-token-abc',
            }),
        };
    });
    afterEach(() => {
        vi.clearAllMocks();
    });
    function createContext() {
        return {
            injector: {
                get(token) {
                    if (token === AuthContextProvider) {
                        return mockAuthProvider;
                    }
                    if (token === Auth0ManagementProvider) {
                        return mockAuth0ManagementProvider;
                    }
                    if (token === InvitationsProvider) {
                        return mockInvitationsProvider;
                    }
                    throw new Error(`Unexpected token requested: ${String(token)}`);
                },
            },
        };
    }
    it('business_owner can create invitation (happy path)', async () => {
        const result = await createInvitation({}, { email: 'new.user@example.com', roleId: 'employee' }, createContext(), mockInfo);
        expect(result).toEqual({
            id: 'inv-123',
            email: 'new.user@example.com',
            roleId: 'employee',
            expiresAt: new Date('2030-01-01T00:00:00.000Z'),
            token: 'invitation-token-abc',
        });
    });
    it('Auth0 Management API called with correct parameters (mock)', async () => {
        await createInvitation({}, { email: 'INVITEE@EXAMPLE.COM', roleId: 'scraper' }, createContext(), mockInfo);
        expect(mockAuth0ManagementProvider.createBlockedUser).toHaveBeenCalledWith('invitee@example.com');
    });
    it('invitation record inserted with correct fields', async () => {
        await createInvitation({}, { email: 'new.user@example.com', roleId: 'accountant' }, createContext(), mockInfo);
        expect(mockInvitationsProvider.insertInvitation).toHaveBeenCalledTimes(1);
        const call = mockInvitationsProvider.insertInvitation.mock.calls[0][0];
        expect(call.email).toBe('new.user@example.com');
        expect(call.roleId).toBe('accountant');
        expect(call.auth0UserId).toBe('auth0|new-user-123');
        expect(call.invitedByUserId).toBe('user-123');
        expect(call.ownerId).toBe('business-123');
    });
    it('cleans up Auth0 user when DB insert fails', async () => {
        const dbError = new Error('DB insert failed');
        mockInvitationsProvider.insertInvitation.mockRejectedValueOnce(dbError);
        await expect(createInvitation({}, { email: 'new.user@example.com', roleId: 'employee' }, createContext(), mockInfo)).rejects.toBe(dbError);
        expect(mockAuth0ManagementProvider.deleteUser).toHaveBeenCalledWith('auth0|new-user-123');
    });
    it('duplicate active invitation rejected', async () => {
        mockInvitationsProvider.getInvitationByEmailLoader.load.mockResolvedValueOnce([
            {
                id: 'existing-invitation',
                email: 'new.user@example.com',
            },
        ]);
        await expect(createInvitation({}, { email: 'new.user@example.com', roleId: 'employee' }, createContext(), mockInfo)).rejects.toMatchObject({
            extensions: { code: 'INVITATION_ALREADY_PENDING' },
        });
        expect(mockAuth0ManagementProvider.createBlockedUser).not.toHaveBeenCalled();
        expect(mockInvitationsProvider.insertInvitation).not.toHaveBeenCalled();
    });
    it('Auth0 rate limit handled gracefully (mocked 429)', async () => {
        mockAuth0ManagementProvider.createBlockedUser.mockRejectedValue({
            statusCode: 429,
            retryAfter: 60,
            message: 'Too many requests',
        });
        await expect(createInvitation({}, { email: 'new.user@example.com', roleId: 'employee' }, createContext(), mockInfo)).rejects.toMatchObject({
            extensions: {
                code: 'RATE_LIMITED',
                retryAfter: 60,
            },
        });
        expect(mockInvitationsProvider.insertInvitation).not.toHaveBeenCalled();
    });
    it('Auth0 user already exists returns USER_ALREADY_EXISTS', async () => {
        mockAuth0ManagementProvider.createBlockedUser.mockRejectedValue({
            statusCode: 409,
            message: 'The user already exists',
        });
        await expect(createInvitation({}, { email: 'new.user@example.com', roleId: 'employee' }, createContext(), mockInfo)).rejects.toMatchObject({
            extensions: {
                code: 'USER_ALREADY_EXISTS',
            },
        });
        expect(mockInvitationsProvider.insertInvitation).not.toHaveBeenCalled();
    });
    it('employee calling mutation gets FORBIDDEN', async () => {
        mockAuthProvider.getAuthContext.mockResolvedValueOnce({
            authType: 'jwt',
            user: {
                userId: 'user-employee',
                roleId: 'employee',
                email: 'employee@example.com',
                permissions: [],
                emailVerified: true,
                permissionsVersion: 1,
                auth0UserId: 'auth0|employee',
            },
            tenant: {
                businessId: 'business-123',
                roleId: 'employee',
            },
        });
        await expect(createInvitation({}, { email: 'new.user@example.com', roleId: 'employee' }, createContext(), mockInfo)).rejects.toSatisfy(error => {
            return error instanceof GraphQLError && error.extensions?.code === 'FORBIDDEN';
        });
        expect(mockInvitationsProvider.insertInvitation).not.toHaveBeenCalled();
    });
});
//# sourceMappingURL=create-invitation.test.js.map