import { createAttributionModule } from './module/Attribution'
import { createDeeplinkModule } from './module/Deeplink'
import { createEventModule } from './module/Event'
import { createFetchModule } from './module/Fetch'
import { createPlacementModule } from './module/Placement'
import { createRegisterModule } from './module/Register'
import { createSwitchModule } from './module/Switch'
import { createWebInterfaceModule } from './module/WebInterface'

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

export const createDependency = () => {}

createDependency.Airbridge = () => ({
    attributionModule: createAttributionModule(),
    deeplinkModule: createDeeplinkModule(),
    eventModule: createEventModule(),
    fetchModule: createFetchModule(),
    placementModule: createPlacementModule(),
    registerModule: createRegisterModule(),
    switchModule: createSwitchModule(),
    webInterfaceModule: createWebInterfaceModule(),
})

export class Airbridge {
    static #dependency = createDependency.Airbridge()

    // attribution

    /**
     * Sets a listener for receiving attribution of install event.
     * @param onReceived Map of attribution is delivered.
     */
    static setOnAttributionReceived(
        onReceived: (attribution: Record<string, string>) => void,
    ): void {
        this.#dependency.attributionModule.setOnAttributionReceived(onReceived)
    }

    // deeplink

    /**
     * Handles deeplink and deferred-deeplink.
     * @param onReceived URL of deeplink is delivered.
     */
    static setOnDeeplinkReceived(
        onReceived: (deeplink: string) => void,
    ): void {
        this.#dependency.deeplinkModule.setOnDeeplinkReceived(onReceived)
    }

    // event

    /**
     * Tracks user behavior through event tracking with attributes.
     * @param category Name of event.
     * @param semanticAttributes Additional attributes of the event that defined by Airbridge.
     * @param customAttributes Additional attributes of the event.
     */
    static trackEvent(
        category: string,
        semanticAttributes?: Record<string, any>,
        customAttributes?: Record<string, any>,
    ): void {
        this.#dependency.eventModule.trackEvent(category, semanticAttributes, customAttributes)
    }

    // fetch

    /**
     * Fetch deviceUUID of SDK.
     * @param onSuccess Callback to be invoked when deviceUUID is successfully handled.
     * @param onFailure Callback to be invoked when any error occurs.
     */
    static fetchDeviceUUID(
        onSuccess: (deviceUUID: string) => void,
        onFailure?: (error: Error) => void
    ): Promise<boolean> {
        return this.#dependency.fetchModule.fetchDeviceUUID(onSuccess, onFailure)
    }

    /**
     * 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.
     */
    static fetchAirbridgeGeneratedUUID(
        onSuccess: (airbridgeGeneratedUUID: string) => void,
        onFailure?: (error: Error) => void
    ): Promise<boolean> {
        return this.#dependency.fetchModule.fetchAirbridgeGeneratedUUID(onSuccess, onFailure)
    }

    /**
     * Indicates whether notification was sent by Airbridge to track uninstall of app.
     * @param notification The notification to check.
     */
    static isUninstallTrackingNotification(
        notification: Record<string, any>,
    ): boolean {
        return this.#dependency.fetchModule.isUninstallTrackingNotification(notification)
    }

    // placement

    /**
     * Notifies that an in-app area within an app has been clicked on by the user.
     * @param trackingLink tracking link uri
     * @param onSuccess Callback to be invoked when tracking link is successfully handled.
     * @param onFailure Callback to be invoked when any error occurs.
     * @return `true` if all of the following conditions are met below, `false` otherwise.
     *  - If the SDK is initialized and enabled.
     *  - If tracking link is successfully handled.
     */
    static click(
        trackingLink: string,
        onSuccess?: () => void,
        onFailure?: (error: Error) => void
    ): Promise<boolean> {
        return this.#dependency.placementModule.click(trackingLink, onSuccess, onFailure)
    }

    /**
     * Notifies that the in-app area within the app has been exposed to the user.
     * @param trackingLink tracking link uri
     * @param onSuccess Callback to be invoked when tracking link is successfully handled.
     * @param onFailure Callback to be invoked when any error occurs.
     * @return `true` if all of the following conditions are met below, `false` otherwise.
     *  - If the SDK is initialized and enabled.
     *  - If tracking link is successfully handled.
     */
    static impression(
        trackingLink: string,
        onSuccess?: () => void,
        onFailure?: ((error: Error) => void)
    ): Promise<boolean> {
        return this.#dependency.placementModule.impression(trackingLink, onSuccess, onFailure)
    }

    // register

    /**
     * Sets the user ID.
     * @param id The user ID.
     */
    static setUserID(id: string): void {
        this.#dependency.registerModule.setUserID(id)
    }

    /**
     * Clear the user ID.
     */
    static clearUserID(): void {
        this.#dependency.registerModule.clearUserID()
    }

    /**
     * Sets the user email.
     * @param email The user email.
     */
    static setUserEmail(email: string): void {
        this.#dependency.registerModule.setUserEmail(email)
    }

    /**
     * Clear the user email.
     */
    static clearUserEmail(): void {
        this.#dependency.registerModule.clearUserEmail()
    }

    /**
     * Sets the user phone number.
     * @param phone The user phone number.
     */
    static setUserPhone(phone: string): void {
        this.#dependency.registerModule.setUserPhone(phone)
    }

    /**
     * Clear the user phone number.
     */
    static clearUserPhone(): void {
        this.#dependency.registerModule.clearUserPhone()
    }

    /**
     * Sets the key, value pair to the user attribute.
     * @param key The key that uniquely identifies the user attribute.
     * @param value The value to set for the user attribute.
     */
    static setUserAttribute(key: string, value: any): void {
        this.#dependency.registerModule.setUserAttribute(key, value)
    }

    /**
     * Removes the user attribute with the given key.
     * @param key The key that uniquely identifies the user attribute.
     */
    static removeUserAttribute(key: string): void {
        this.#dependency.registerModule.removeUserAttribute(key)
    }

    /**
     * Clears all user attributes.
     */
    static clearUserAttributes(): void {
        this.#dependency.registerModule.clearUserAttributes()
    }

    /**
     * Sets the key, value pair to the user alias.
     * @param key The key that uniquely identifies the user alias.
     * @param value The value to set for the user alias.
     */
    static setUserAlias(key: string, value: string): void {
        this.#dependency.registerModule.setUserAlias(key, value)
    }

     /**
     * Removes the user alias with the given key.
     * @param key The key that uniquely identifies the user alias.
     */
     static removeUserAlias(key: string): void {
        this.#dependency.registerModule.removeUserAlias(key)
    }

    /**
     * Clears all user aliases.
     */
    static clearUserAlias(): void {
        this.#dependency.registerModule.clearUserAlias()
    }

    /**
     * Clears all user information.
     */
    static clearUser(): void {
        this.#dependency.registerModule.clearUser()
    }

    /**
     * Sets the key, value pair to the device alias.
     * @param key The key that uniquely identifies the device alias.
     * @param value The value to set for the device alias.
     */
    static setDeviceAlias(key: string, value: string): void {
        this.#dependency.registerModule.setDeviceAlias(key, value)
    }

    /**
     * Removes the device alias with the given key.
     * @param key The key that uniquely identifies the device alias.
     */
    static removeDeviceAlias(key: string): void {
        this.#dependency.registerModule.removeDeviceAlias(key)
    }

    /**
     * Clears all device aliases.
     */
    static clearDeviceAlias(): void {
        this.#dependency.registerModule.clearDeviceAlias()
    }

    /**
     * Registers the FCM or APNS registration token to track app uninstalls.
     * @param token The FCM or APNS registration token.
     */
    static registerPushToken(token: string): void {
        this.#dependency.registerModule.registerPushToken(token)
    }

    // switch

    /**
     * Enables the SDK.
     */
    static enableSDK(): void {
        this.#dependency.switchModule.enableSDK()
    }

    /**
     * Disables the SDK.
     */
    static disableSDK(): void {
        this.#dependency.switchModule.disableSDK()
    }

    /**
     * Checks whether the SDK is currently enabled.
     * @return `true` if the SDK is enabled, `false` otherwise.
     */
    static isSDKEnabled(): Promise<boolean> {
        return this.#dependency.switchModule.isSDKEnabled()
    }

    /**
     * Start collecting and transferring events.
     */
    static startTracking(): void {
        this.#dependency.switchModule.startTracking()
    }

    /**
     * Stop collecting and transferring events.
     */
    static stopTracking(): void {
        this.#dependency.switchModule.stopTracking()
    }

    /**
     * Checks whether the tracking feature of SDK is currently enabled.
     * @return `true` if the tracking feature of SDK tracking is enabled,`false` otherwise.
     */
    static isTrackingEnabled(): Promise<boolean> {
        return this.#dependency.switchModule.isTrackingEnabled()
    }

    // web interface

    /**
     * Creates a script that initialize the web interface.
     * @param webToken The token to initialize Airbridge Web SDK.
     * @param postMessageScript The JavaScript code to post commands from web to app.
     * @return web interface script
     */
    static createWebInterfaceScript(
        webToken: string,
        postMessageScript: string,
    ): Promise<string|undefined> {
        return this.#dependency.webInterfaceModule.createWebInterfaceScript(webToken, postMessageScript)
    }

    /**
     * Handles commands from the web interface.
     * @param command The command to handle.
     */
    static handleWebInterfaceCommand(command: string): void {
        this.#dependency.webInterfaceModule.handleWebInterfaceCommand(command)
    }

    /**
     * Creates a tracking-link using airbridge-server that move user
     * to specific page of app and track click-event.
     * 
     * @param channel The channel of tracking-link.
     * @param option The option to create tracking-link.
     * @param onSuccess Created tracking-link is delivered if succeed.
     * @param onFailure Error is delivered if failed.
     */
    static createTrackingLink(
        channel: string,
        option: Record<string, any>,
        onSuccess: (airbridgeTrackingLink: AirbridgeTrackingLink) => void,
        onFailure?: (error: Error) => void
    ): void {
        this.#dependency.placementModule.createTrackingLink(channel, option, onSuccess, onFailure)
    }

    /**
     * Starts tracking event automatically for each user
     * purchases product through in-app-purchase.
     */
    static startInAppPurchaseTracking() {
        this.#dependency.switchModule.startInAppPurchaseTracking();
    }

    /**
     * Stops tracking event automatically for each user
     * purchases product through in-app-purchase.
     */
    static stopInAppPurchaseTracking() {
        this.#dependency.switchModule.stopInAppPurchaseTracking();
    }

    /**
     * Indicates that SDK can track event automatically for each user
     * purchases product through in-app-purchase.
     *
     * @return `true` if the SDK is enabled for in-app-purchase tracking, `false` otherwise.
     */
    static isInAppPurchaseTrackingEnabled(): Promise<boolean> {
        return this.#dependency.switchModule.isInAppPurchaseTrackingEnabled()
    }
}