import { createYoga } from 'graphql-yoga';
import { useGraphQLModules } from '@envelop/graphql-modules';
import { createApplication, createModule, gql, Scope } from 'graphql-modules';
import { describe, expect, it, vi } from 'vitest';
import { AuthContextProvider } from '../../../auth/providers/auth-context.provider.js';
import { TenantAwareDBClient } from '../../../app-providers/tenant-db-client.js';
import { ChargesAuthorizationProvider } from '../charges-authorization.provider.js';
function createMockAuthProvider(roleId) {
    return {
        getAuthContext: vi.fn().mockResolvedValue({
            authType: 'jwt',
            user: {
                userId: 'user-1',
                email: `${roleId}@example.com`,
                roleId,
                permissions: [],
                emailVerified: true,
                permissionsVersion: 1,
                auth0UserId: `auth0|${roleId}`,
            },
            tenant: {
                businessId: 'biz-1',
                roleId,
            },
        }),
    };
}
function createService(roleId, existingChargeIds = ['charge-1']) {
    const authProvider = createMockAuthProvider(roleId);
    const db = {
        query: vi.fn().mockImplementation((_query, values) => {
            const chargeIds = Array.isArray(values?.[0]) ? values?.[0] : [];
            const rows = chargeIds
                .filter(chargeId => existingChargeIds.includes(chargeId))
                .map(id => ({ id }));
            return Promise.resolve({ rows });
        }),
    };
    return new ChargesAuthorizationProvider(authProvider, db);
}
describe('ChargesAuthorizationProvider', () => {
    it('canReadCharges passes for all roles', async () => {
        for (const role of ['business_owner', 'accountant', 'employee', 'scraper']) {
            const service = createService(role);
            await expect(service.canReadCharges()).resolves.toBeUndefined();
        }
    });
    it('canReadCharges rejects with UNAUTHENTICATED when there is no user in auth context', async () => {
        const authProvider = {
            getAuthContext: vi.fn().mockResolvedValue({
                authType: 'jwt',
                user: null,
                tenant: null,
            }),
        };
        const db = {
            query: vi.fn(),
        };
        const service = new ChargesAuthorizationProvider(authProvider, db);
        await expect(service.canReadCharges()).rejects.toMatchObject({
            extensions: { code: 'UNAUTHENTICATED' },
        });
    });
    it('canWriteCharge allows business_owner, accountant, scraper and gmail_listener; blocks employee', async () => {
        await expect(createService('business_owner').canWriteCharge()).resolves.toBeUndefined();
        await expect(createService('accountant').canWriteCharge()).resolves.toBeUndefined();
        await expect(createService('scraper').canWriteCharge()).resolves.toBeUndefined();
        await expect(createService('gmail_listener').canWriteCharge()).resolves.toBeUndefined();
        await expect(createService('employee').canWriteCharge()).rejects.toMatchObject({
            extensions: { code: 'FORBIDDEN' },
        });
    });
    it('canDeleteCharge allows business_owner and accountant; returns NOT_FOUND for unknown charge', async () => {
        await expect(createService('business_owner').canDeleteCharge('charge-1')).resolves.toBeUndefined();
        await expect(createService('accountant').canDeleteCharge('charge-1')).resolves.toBeUndefined();
        await expect(createService('employee').canDeleteCharge('charge-1')).rejects.toMatchObject({
            extensions: { code: 'FORBIDDEN' },
        });
        await expect(createService('scraper').canDeleteCharge('charge-1')).rejects.toMatchObject({
            extensions: { code: 'FORBIDDEN' },
        });
        await expect(createService('business_owner', []).canDeleteCharge('missing-charge')).rejects.toMatchObject({
            extensions: { code: 'NOT_FOUND' },
        });
    });
    it('canDeleteChargesByIds checks all ids in a single query and returns NOT_FOUND if any is missing', async () => {
        await expect(createService('business_owner', ['charge-1', 'charge-2']).canDeleteChargesByIds([
            'charge-1',
            'charge-2',
        ])).resolves.toBeUndefined();
        await expect(createService('business_owner', ['charge-1']).canDeleteChargesByIds(['charge-1', 'missing-charge'])).rejects.toMatchObject({
            extensions: { code: 'NOT_FOUND' },
        });
    });
    it('integration: employee calling deleteCharge mutation gets FORBIDDEN', async () => {
        const testModule = createModule({
            id: 'charges-authorization-integration-test',
            typeDefs: gql `
        type Mutation {
          deleteCharge(chargeId: ID!): Boolean!
        }

        type Query {
          _empty: String
        }
      `,
            resolvers: {
                Mutation: {
                    deleteCharge: async (_, args, context) => {
                        await context.injector.get(ChargesAuthorizationProvider).canDeleteCharge(args.chargeId);
                        return true;
                    },
                },
            },
            providers: [
                ChargesAuthorizationProvider,
                {
                    provide: AuthContextProvider,
                    useFactory: () => createMockAuthProvider('employee'),
                    scope: Scope.Operation,
                },
                {
                    provide: TenantAwareDBClient,
                    useFactory: () => ({
                        query: vi.fn().mockResolvedValue({ rows: [{ id: 'charge-1' }] }),
                    }),
                    scope: Scope.Operation,
                },
            ],
        });
        const app = createApplication({ modules: [testModule] });
        const yoga = createYoga({
            maskedErrors: false,
            plugins: [useGraphQLModules(app)],
        });
        const response = await yoga.fetch('http://localhost:4000/graphql', {
            method: 'POST',
            headers: { 'content-type': 'application/json' },
            body: JSON.stringify({
                query: 'mutation { deleteCharge(chargeId: "charge-1") }',
            }),
        });
        const result = await response.json();
        expect(result.data).toBeNull();
        expect(result.errors?.[0]?.extensions?.code).toBe('FORBIDDEN');
    });
});
//# sourceMappingURL=charges-authorization.test.js.map