import { createAuthMethods } from '../auth'

describe('createAuthMethods', () => {
    const mockClient = {
        auth: {
            getAuthenticationUrl: jest
                .fn()
                .mockReturnValue('https://dropbox.com/auth'),
            getAccessTokenFromCode: jest.fn().mockResolvedValue({
                result: {
                    access_token: 'new-access-token',
                    refresh_token: 'new-refresh-token',
                    expires_in: 14400,
                },
            }),
        },
    }

    const mockGetClient = jest.fn().mockReturnValue(mockClient)
    const mockSetAccessToken = jest.fn()
    const mockCredentials = {
        clientId: 'test-client-id',
        clientSecret: 'test-client-secret',
        accessToken: 'test-access-token',
        refreshToken: 'test-refresh-token',
    }

    beforeEach(() => {
        jest.clearAllMocks()
        // Mock fetch for refreshAccessToken method
        global.fetch = jest.fn().mockResolvedValue({
            ok: true,
            json: jest.fn().mockResolvedValue({
                access_token: 'refreshed-token',
                refresh_token: 'new-refresh-token',
                expires_in: 14400,
            }),
        })
    })

    it('should create auth methods object', () => {
        const auth = createAuthMethods(
            mockGetClient,
            mockCredentials,
            mockSetAccessToken
        )

        expect(auth).toHaveProperty('getAuthUrl')
        expect(auth).toHaveProperty('exchangeCodeForToken')
        expect(auth).toHaveProperty('refreshAccessToken')
    })

    it('should generate auth URL', async () => {
        const auth = createAuthMethods(
            mockGetClient,
            mockCredentials,
            mockSetAccessToken
        )

        const url = await auth.getAuthUrl(
            'http://localhost/callback',
            'test-state'
        )

        expect(mockGetClient).toHaveBeenCalled()
        expect(mockClient.auth.getAuthenticationUrl).toHaveBeenCalledWith(
            'http://localhost/callback',
            'test-state',
            'code',
            'offline',
            undefined,
            undefined,
            true
        )
        expect(url).toEqual('https://dropbox.com/auth')
    })

    it('should exchange code for token', async () => {
        const auth = createAuthMethods(
            mockGetClient,
            mockCredentials,
            mockSetAccessToken
        )

        const tokens = await auth.exchangeCodeForToken(
            'auth-code',
            'http://localhost/callback'
        )

        expect(mockGetClient).toHaveBeenCalled()
        expect(mockClient.auth.getAccessTokenFromCode).toHaveBeenCalledWith(
            'http://localhost/callback',
            'auth-code'
        )
        expect(mockSetAccessToken).toHaveBeenCalledWith('new-access-token')
        expect(tokens).toEqual({
            accessToken: 'new-access-token',
            refreshToken: 'new-refresh-token',
            expiresAt: expect.any(Number),
        })
    })

    it('should refresh access token', async () => {
        const auth = createAuthMethods(
            mockGetClient,
            mockCredentials,
            mockSetAccessToken
        )

        const tokens = await auth.refreshAccessToken()

        expect(fetch).toHaveBeenCalledWith(
            'https://api.dropboxapi.com/oauth2/token',
            {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: expect.stringContaining('grant_type=refresh_token'),
            }
        )

        expect(mockSetAccessToken).toHaveBeenCalledWith('refreshed-token')
        expect(tokens).toEqual({
            accessToken: 'refreshed-token',
            refreshToken: 'new-refresh-token',
            expiresAt: expect.any(Number),
        })
    })

    it('should throw error if refresh token is missing', async () => {
        const auth = createAuthMethods(
            mockGetClient,
            { clientId: 'test-id', clientSecret: 'test-secret' },
            mockSetAccessToken
        )

        await expect(auth.refreshAccessToken()).rejects.toThrow(
            'Refresh token, client ID, and client secret are required to refresh access token'
        )
    })
})
