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

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

import { AirbridgeTrackingLink } from '../type/AirbridgeTrackingLink'

export const createDependency = () => { }

type PlacementInteractor = {
    click(
        promiseID: string,
        trackingLink: string,
        onSuccess: () => void,
        onFailure: (message: string) => void,
    ): Promise<boolean>

    impression(
        promiseID: string,
        trackingLink: string,
        onSuccess: () => void,
        onFailure: (message: string) => void,
    ): Promise<boolean>

    createTrackingLink(
        channel: string,
        option: Record<string, any>,
        onSuccess: (airbridgeTrackingLink: Record<string, string>) => void,
        onFailure: (message: string) => void,
    ): void
}

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

export type PlacementModule = ReturnType<typeof createPlacementModule>

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

    // define member
    let id = 0

    // define method
    const click = async (
        trackingLink: string,
        onSuccess?: () => void,
        onFailure?: (error: Error) => void
    ): Promise<boolean> => {
        if (!check.string(trackingLink)) {
            log.unmatchedType('trackingLink', 'string')
            return false
        }
        if (!(check.function(onSuccess) || check.undefined(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.placement', (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.click(
                promiseID,
                trackingLink,
                () => { onSuccess?.() },
                (message: string) => { onFailure?.(Error(message)) }
            )
        })
    }

    const impression = async (
        trackingLink: string,
        onSuccess?: () => void,
        onFailure?: (error: Error) => void
    ): Promise<boolean> => {
        if (!check.string(trackingLink)) {
            log.unmatchedType('trackingLink', 'string')
            return false
        }
        if (!(check.function(onSuccess) || check.undefined(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.placement', (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.impression(
                promiseID,
                trackingLink,
                () => { onSuccess?.() },
                (message: string) => { onFailure?.(Error(message)) }
            )
        })
    }

    const createTrackingLink = (
        channel: string,
        option: Record<string, any>,
        onSuccess: (airbridgeTrackingLink: AirbridgeTrackingLink) => void,
        onFailure?: (error: Error) => void
    ): void => {
        if (!check.string(channel)) {
            log.unmatchedType('channel', 'string')
            return
        }
        if (!(check.undefined(option) || check.object(option))) {
            log.unmatchedType('option', 'object')
            return
        }
        if (!check.function(onSuccess)) {
            log.unmatchedType('onSuccess', 'function?')
            return
        }
        if (!(check.function(onFailure) || check.undefined(onFailure))) {
            log.unmatchedType('onFailure', 'function?')
            return
        }

        interactor.createTrackingLink(
            channel,
            option,
            (airbridgeTrackingLink: Record<string, string>) => {
                if (
                    check.string(airbridgeTrackingLink['qrcodeURL']) &&
                    check.string(airbridgeTrackingLink['shortURL'])
                ) {
                    onSuccess?.({
                        qrcodeURL: airbridgeTrackingLink['qrcodeURL'],
                        shortURL: airbridgeTrackingLink['shortURL']
                    })
                }
            },
            (message: string) => { onFailure?.(Error(message)) }
        )
    }

    // create object
    return {
        click,
        impression,
        createTrackingLink
    }
}
