import {
    NativeModules,
    NativeEventEmitter,
    EmitterSubscription,
    NativeModule,
    NativeModulesStatic,
} from 'react-native';

interface BlazeAsyncBridgeInterface {

    /**
    * Register a handler for a JavaScript method that can be called from native
    * @param methodName The name of the method to register
    * @param handler A function that handles the method call and returns a Promise
    */
    registerJSMethod(methodName: string, handler: BlazeJSMethodHandler): void

    /**
     * Unregister a JavaScript method handler
     * @param methodName The name of the method to unregister
     */
    unregisterJSMethod(methodName: string): void
}

interface AsyncBridgeNativeInterface
    extends NativeModulesStatic,
    NativeModule {
    resolveJSResponse(response: {
        callbackId: string;
        success: boolean;
        data?: string | null;
        errorMessage?: string;
    }): Promise<void>;
}

const { RTNBlazeAsyncBridge } = NativeModules;
const BlazeAsyncBridgeNativeModule = RTNBlazeAsyncBridge as AsyncBridgeNativeInterface;

type BlazeJSMethodHandler = (
    params: unknown,
    callbackId: string
) => Promise<any>;

class BlazeAsyncBridgeWrapper {
    private methodHandlers: Record<string, BlazeJSMethodHandler> = {};
    private bridgeAsyncFunctionName = 'BlazeAsyncBridge.jsRequest';
    private eventEmitter: NativeEventEmitter;
    private eventSubscription: EmitterSubscription | null = null;

    // Singleton instance
    private static instance: BlazeAsyncBridgeWrapper;

    // Private constructor for singleton
    private constructor() {
        this.eventEmitter = new NativeEventEmitter(BlazeAsyncBridgeNativeModule);

        // Subscribe to events from the native module
        this.eventSubscription = this.eventEmitter.addListener(
            this.bridgeAsyncFunctionName,
            this.handleJSRequest
        );
    }

    // Method to get singleton instance
    static getInstance(): BlazeAsyncBridgeWrapper {
        if (!this.instance) {
            this.instance = new BlazeAsyncBridgeWrapper();
        }
        return this.instance;
    }

    registerJSMethod(methodName: string, handler: BlazeJSMethodHandler): void {
        this.methodHandlers[methodName] = handler;
    }

    unregisterJSMethod(methodName: string): void {
        delete this.methodHandlers[methodName];
    }

    /**
     * Clear all registered handlers - useful for hot reload (internal use only)
     */
    clearHandlers(): void {
        this.methodHandlers = {};
    }

    /**
     * Handle a JS method request from native
     * @param event The event data containing method name, parameters, and callback ID
     */
    private handleJSRequest = async (event: {
        methodName: string;
        params: string; // JSON string from native
        callbackId: string;
    }) => {
        const { methodName, params, callbackId } = event;
        const handler = this.methodHandlers[methodName];

        if (!handler) {
            this.resolveJSResponse({
                callbackId,
                success: false,
                errorMessage: `No handler registered for method: ${methodName}`,
            });
            return;
        }

        try {
            // Parse JSON string to get the typed object
            let parsedParams: unknown;
            try {
                parsedParams = JSON.parse(params);
            } catch (parseError) {
                this.resolveJSResponse({
                    callbackId,
                    success: false,
                    errorMessage: `Failed to parse JSON params: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
                });
                return;
            }

            // Call the handler with the parsed typed parameters
            const result = await handler(parsedParams, callbackId);
            // Serialize result to JSON string for consistent cross-platform handling
            const jsonData = result !== undefined ? JSON.stringify(result) : null;
            this.resolveJSResponse({
                callbackId,
                success: true,
                data: jsonData,
            });
        } catch (error) {
            this.resolveJSResponse({
                callbackId,
                success: false,
                errorMessage: error instanceof Error ? JSON.stringify(error) : String(error),
            });
        }
    };

    /**
     * Send a response back to native code
     * @param response The response data
     */
    private resolveJSResponse(response: {
        callbackId: string;
        success: boolean;
        data?: string | null;
        errorMessage?: string;
    }): void {
        BlazeAsyncBridgeNativeModule.resolveJSResponse(response);
    }

}

export const BlazeAsyncBridge = BlazeAsyncBridgeWrapper.getInstance() as BlazeAsyncBridgeInterface;

// Hot reload handling - clear handlers only, keep event subscription intact
if (__DEV__) {
    BlazeAsyncBridgeWrapper.getInstance().clearHandlers();
} 