import { NativeModules } from 'react-native'
const { RNBranch } = NativeModules
import { BranchSubscriber } from 'react-native-branch'

test('default initializes with no options', () => {
  const subscriber = new BranchSubscriber(null)
  expect(subscriber.options).toEqual({})
})

test('stores options passed to the constructor', () => {
  const subscriber = new BranchSubscriber({
    checkCachedEvents: false
  })

  expect(subscriber.options).toEqual({ checkCachedEvents: false })
})

test('subscribes to init session start events', () => {
  const subscriber = new BranchSubscriber({
    checkCachedEvents: false,
    onOpenStart: ({uri}) => {},
  })
  subscriber._nativeEventEmitter.addListener = jest.fn((eventType, listener) => {})
  subscriber.subscribe()

  expect(subscriber._nativeEventEmitter.addListener.mock.calls.length).toBe(1)
  expect(subscriber._nativeEventEmitter.addListener.mock.calls[0][0]).toBe(RNBranch.INIT_SESSION_START)
  expect(subscriber._nativeEventEmitter.addListener.mock.calls[0][1]).toBe(subscriber.options.onOpenStart)
})

test('subscribes to init session success & error events', () => {
  const subscriber = new BranchSubscriber({
    checkCachedEvents: false,
    onOpenComplete: ({params, error, uri}) => {},
  })
  subscriber._nativeEventEmitter.addListener = jest.fn((eventType, listener) => {})
  subscriber.subscribe()

  expect(subscriber._nativeEventEmitter.addListener.mock.calls.length).toBe(2)

  // This comparison ignores the call order.
  const mockArgs = subscriber._nativeEventEmitter.addListener.mock.calls.map(call => call[0]).sort()
  expect(mockArgs).toEqual([
    RNBranch.INIT_SESSION_ERROR,
    RNBranch.INIT_SESSION_SUCCESS,
  ])
  expect(subscriber._nativeEventEmitter.addListener.mock.calls[0][1]).toBe(subscriber.options.onOpenComplete)
  expect(subscriber._nativeEventEmitter.addListener.mock.calls[1][1]).toBe(subscriber.options.onOpenComplete)
})

// async test
test('will return a cached event when appropriate', done => {
  const mockResult = {
    params: {
      '+clicked_branch_link': false,
      '+is_first_session': false,
    },
    error: null,
    uri: null,
  }

  // expectedResult is mockResult with +rn_cached_initial_event added to
  // params.
  const expectedParams = {
    ...mockResult.params,
    '+rn_cached_initial_event': true,
  }
  const expectedResult = {
    params: expectedParams,
    error: mockResult.error,
    uri: mockResult.uri,
  }

  // Mock promise from redeemInitSessionResult
  RNBranch.redeemInitSessionResult.mockReturnValueOnce(Promise.resolve(mockResult))

  // Set up subscriber, mocking the callbacks
  const subscriber = new BranchSubscriber({
    checkCachedEvents: true,
    onOpenStart: jest.fn(({uri}) => {}),
    onOpenComplete: jest.fn(({params, error, uri}) => {}),
  })

  // mock subscriber._nativeEventEmitter.addListener. Expect this to be called three times.
  let addListenerCount = 0
  subscriber._nativeEventEmitter.addListener = (eventType, listener) => {
    if (++addListenerCount < 3) return

    // --- Check results ---

    try {
      // Expect onOpenStart and onOpenComplete both to be called

      // uri passed to onOpenStart
      expect(subscriber.options.onOpenStart.mock.calls.length).toBe(1)
      expect(subscriber.options.onOpenStart.mock.calls[0][0]).toEqual({uri: null})

      // full result passed to onOpenComplete with +rn_cached_initial_event: true
      expect(subscriber.options.onOpenComplete.mock.calls.length).toBe(1)
      const actualResult = subscriber.options.onOpenComplete.mock.calls[0][0]
      expect(actualResult).toEqual(expectedResult)

      // state cleared
      expect(subscriber._checkCachedEvents).toBe(false)

      done()
    } catch(error) {
      done(error)
    }
  }
  expect(subscriber._checkCachedEvents).toBe(true)

  // --- Code under test ---
  subscriber.subscribe()
})

test('passes a non-null uri to onOpenStart when available', done => {
  const mockResult = {
    params: {
      '+clicked_branch_link': true,
      '+is_first_session': false,
    },
    error: null,
    uri: 'https://abcd.app.link/xyz',
  }

  // Mock promise from redeemInitSessionResult
  RNBranch.redeemInitSessionResult.mockReturnValueOnce(Promise.resolve(mockResult))

  // Set up subscriber, mocking the callbacks
  const subscriber = new BranchSubscriber({
    checkCachedEvents: true,
    onOpenStart: jest.fn(({uri}) => {}),
  })

  // mock subscriber._nativeEventEmitter.addListener.
  subscriber._nativeEventEmitter.addListener = (eventType, listener) => {
    // --- Check results ---

    try {
      // Expect onOpenStart to be called
      // uri passed to onOpenStart
      expect(subscriber.options.onOpenStart.mock.calls.length).toBe(1)
      expect(subscriber.options.onOpenStart.mock.calls[0][0]).toEqual({uri: mockResult.uri})

      done()
    } catch(error) {
      done(error)
    }
  }

  // --- Code under test ---
  subscriber.subscribe()
})

test('does not return a cached result when none available', done => {
  // Mock promise from redeemInitSessionResult
  RNBranch.redeemInitSessionResult.mockReturnValueOnce(Promise.resolve(null))

  // Set up subscriber, mocking the callbacks
  const subscriber = new BranchSubscriber({
    checkCachedEvents: true,
    onOpenStart: jest.fn(({uri}) => {}),
    onOpenComplete: jest.fn(({params, error, uri}) => {}),
  })

  // mock subscriber._nativeEventEmitter.addListener. Expect this to be called three times.
  let addListenerCount = 0
  subscriber._nativeEventEmitter.addListener = (eventType, listener) => {
    if (++addListenerCount < 3) return

    // --- Check results ---

    try {
      // Expect onOpenStart, onOpenComplete not to be called
      expect(subscriber.options.onOpenStart.mock.calls.length).toBe(0)
      expect(subscriber.options.onOpenComplete.mock.calls.length).toBe(0)

      done()
    } catch(error) {
      done(error)
    }
  }

  // --- Code under test ---
  subscriber.subscribe()
})

@@ -2243,19 +2243,54 @@
 cted
-P
+Result = %7B%0A    ...mockResult,%0A    p
 arams
- =
+:
  %7B%0A
+  
     
@@ -2307,24 +2307,26 @@
 ult.params,%0A
+  
     '+rn_cac
@@ -2357,118 +2357,11 @@
 ,%0A  
-%7D%0A  const expectedResult = %7B%0A    params: expectedParams,%0A    error: mockResult.error,%0A    uri: mockResult.uri,
+  %7D
 %0A  %7D

Simplify expectedResult expression