import { NativeEventEmitter, NativeModules } from 'react-native'

import { createInteractor } from '../architecture/Interactor'
import { check } from '../utility/check'
import { log } from '../utility/log'

export const createDependency = () => {}

type FetchInteractor = {
    fetchDeviceUUID(
        promiseID: string,
        onSuccess: (deviceUUID: string) => void,
        onFailure: (message: string) => void,
    ): Promise<boolean>
    fetchAirbridgeGeneratedUUID(
        promiseID: string,
        onSuccess: (airbridgeGeneratedUUID: string) => void,
        onFailure: (message: string) => void,
    ): Promise<boolean>
}

createDependency.FetchModule = () => ({
    emitter: new NativeEventEmitter(NativeModules.FetchInteractor),
    interactor: createInteractor<FetchInteractor>(NativeModules.FetchInteractor),
})

export type FetchModule = ReturnType<typeof createFetchModule>

export const createFetchModule= () => {
    // create dependency
    const { emitter, interactor } = createDependency.FetchModule()

    // define member
    let id = 0

    // define method
    const fetchDeviceUUID = async (
        onSuccess: (deviceUUID: string) => void,
        onFailure?: (error: Error) => void
    ): Promise<boolean> => {
        if (!check.function(onSuccess)) {
            log.unmatchedType('onSuccess', 'function?')
            return false
        }
        if (!(check.function(onFailure) || check.undefined(onFailure))) {
            log.unmatchedType('onFailure', 'function?')
            return false
        }

        const promiseID = `${id++}`

        return new Promise((onResolve, onReject) => {
            const subscription = emitter.addListener('airbridge.fetch', (message) => {
                if (message.id !== promiseID) {
                    return
                }
                switch (message.type) {
                    case 'onResolve':
                        onResolve(message.value)
                        break
                    case 'onReject':
                        onReject(Error(message.value))
                        break
                }
                subscription.remove()
            })

            interactor.fetchDeviceUUID(
                promiseID,
                (deviceUUID) => { onSuccess(deviceUUID) },
                (message) => { onFailure?.(Error(message)) },
            )
        })
    }

    /**
     * Fetch airbridgeGeneratedUUID of SDK.
     * @param onSuccess Callback to be invoked when airbridgeGeneratedUUID is successfully handled.
     * @param onFailure Callback to be invoked when any error occurs.
     */
    const fetchAirbridgeGeneratedUUID = async (
        onSuccess: (airbridgeGeneratedUUID: string) => void,
        onFailure?: (error: Error) => void
    ): Promise<boolean> => {
        if (!check.function(onSuccess)) {
            log.unmatchedType('onSuccess', 'function?')
            return false
        }
        if (!(check.function(onFailure) || check.undefined(onFailure))) {
            log.unmatchedType('onFailure', 'function?')
            return false
        }

        const promiseID = `${id++}`

        return new Promise((onResolve, onReject) => {
            const subscription = emitter.addListener('airbridge.fetch', (message) => {
                if (message.id !== promiseID) {
                    return
                }
                switch (message.type) {
                    case 'onResolve':
                        onResolve(message.value)
                        break
                    case 'onReject':
                        onReject(Error(message.value))
                        break
                }
                subscription.remove()
            })

            interactor.fetchAirbridgeGeneratedUUID(
                promiseID,
                (airbridgeGeneratedUUID) => { onSuccess(airbridgeGeneratedUUID) },
                (message) => { onFailure?.(Error(message)) },
            )
        })
    }

    const isUninstallTrackingNotification = (
        notification: Record<string, any>,
    ): boolean => {
        if (!check.object(notification)) {
            log.unmatchedType('notification', 'object')
            return false
        }

        return check.defined(notification['airbridge-uninstall-tracking'])
    }

    // create object
    return {
        fetchDeviceUUID,
        fetchAirbridgeGeneratedUUID,
        isUninstallTrackingNotification,
    }
}
