import {
    IBlazeAnalytics,
    BlazeError
} from '../interfaces';
import { BlazeGlobalEvents } from '../shared/BlazeGlobalEvents';
import { parseBlazeError } from '../interfaces/blaze-error.interface';
import { BlazeAsyncBridge } from '../shared/BlazeAsyncBridge';

export interface BlazeGlobalDelegate {
    /**
     * Called when an analytics event is triggered by the SDK.
     *
     * @param params - Contains the analytics event data.
     */
    onEventTriggered?: (params: BlazeOnEventTriggeredParams) => void
    /**
     * Called when an error is thrown by the SDK.
     *
     * @param params - Contains the error details.
     */
    onErrorThrown?: (params: BlazeOnErrorThrownParams) => void
    /**
     * Called before playing HLS or MP4 content to allow playback modification (e.g., URL tokenization, header modification).
     * If not implemented, the original content will be used without modification.
     *
     * Unlike on Native, this can't be async because on Android our full screen player stop the JS thread from executing (ReactNative limitation).
     *
     * @param request - Contains the original URL.
     * @returns the modified playback response.
     */
    playbackModificationHandler?: (
        request: BlazePlaybackModificationRequest
    ) => BlazePlaybackModificationResponse;
}

export interface BlazeOnEventTriggeredParams {
    event: IBlazeAnalytics;
}

export interface BlazeOnErrorThrownParams {
    error: BlazeError;
}

export interface BlazePlaybackModificationRequest {
    originalURL: string;
}

export interface BlazePlaybackModificationResponse {
    modifiedURL: string;
}

export class BlazeGlobalDelegateHelper {

    static registerGlobalDelegate(globalDelegate?: BlazeGlobalDelegate | null) {
        BlazeGlobalDelegateHelper.onEventTriggered(globalDelegate?.onEventTriggered)
        BlazeGlobalDelegateHelper.onErrorThrown(globalDelegate?.onErrorThrown)
        BlazeGlobalDelegateHelper.playbackModificationHandler(globalDelegate?.playbackModificationHandler)
    }

    private static playbackModificationHandler(
        callback?: (request: BlazePlaybackModificationRequest) => BlazePlaybackModificationResponse,
    ) {
        const methodName = 'Blaze.GlobalDelegate.playbackModificationHandler'
        if (callback) {
            BlazeAsyncBridge.registerJSMethod(
                methodName,
                async (params) => {
                    const request = params as BlazePlaybackModificationRequest
                    try {
                        return await callback(request)
                    } catch (error) {
                        console.error(`Error in ${methodName}:`, error)
                        return { modifiedURL: request.originalURL ?? '' }
                    }
                },
            )
        } else {
            BlazeAsyncBridge.unregisterJSMethod(methodName)
        }
    }

    private static onEventTriggered(
        callback?: (params: BlazeOnEventTriggeredParams) => void,
    ) {
        const eventName = 'Blaze.onEventTriggered'
        if (callback) {
            BlazeGlobalEvents.createEventSubscription(
                eventName,
                (data: any) => {
                    try {
                        if (data) {
                            const parsed = JSON.parse(data['eventData']);
                            callback({
                                event: parsed as IBlazeAnalytics
                            });
                        } else {
                            console.error('onEventTriggered: was called with no data.');
                        }
                    } catch (e) {
                        console.error('onEventTriggered', e);
                    }
                },
            );
        } else {
            BlazeGlobalEvents.cancelEventSubscription(eventName)
        }

    }

    private static onErrorThrown(
        callback?: (params: BlazeOnErrorThrownParams) => void,
    ) {
        const eventName = 'Blaze.onErrorThrown'
        if (callback) {
            BlazeGlobalEvents.createEventSubscription(
                eventName,
                (data: any) => {
                    try {
                        const error = parseBlazeError(data['error']);
                        if (error) {
                            callback({
                                error: error
                            })
                        } else {
                            console.error('onErrorThrown: failed to parse error from data:', data);
                        }
                    } catch (e) {
                        console.error('onErrorThrown', e);
                    }
                },
            );
        } else {
            BlazeGlobalEvents.cancelEventSubscription(eventName)
        }
    }

}