const { withDangerousMod } = require("@expo/config-plugins");
const fs = require("fs");
const path = require("path");

// const packageName = "net.alpla.lst.mobile";
// const packagePath = "com/alpla/lst/mobile";
const packageName = "net.alpla.lst.mobile";
const packagePath = "net/alpla/lst/mobile";
// const packageName = config.android?.package;
// const packagePath = packageName.replace(/\./g, "/");

const moduleCode = `package ${packageName}.scanner

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import com.facebook.react.bridge.*
import com.facebook.react.modules.core.DeviceEventManagerModule

class ZebraScannerModule(
    private val reactContext: ReactApplicationContext
) : ReactContextBaseJavaModule(reactContext) {

    override fun getName(): String = "ZebraScanner"

    private val scanAction = "com.lst.mobile.SCAN"
    private var receiverRegistered = false

    private val scanReceiver = object : BroadcastReceiver() {
       override fun onReceive(context: Context?, intent: Intent?) {
            println("LST SCANNER: received intent -> \${intent?.action}")

            if (intent?.action != scanAction) {
                println("LST SCANNER: wrong action")
                return
            }

            val barcodeData: String? =
                intent.getStringExtra("com.symbol.datawedge.data_string")

            val labelType: String? =
                intent.getStringExtra("com.symbol.datawedge.label_type")

            val source: String? =
                intent.getStringExtra("com.symbol.datawedge.source")

            println("LST SCANNER: data=$barcodeData label=$labelType source=$source")

            if (barcodeData.isNullOrBlank()) {
                println("LST SCANNER: empty barcode")
                return
            }

            val payload = Arguments.createMap().apply {
                putString("data", barcodeData)
                putString("labelType", labelType)
                putString("source", source)
                putDouble("timestamp", System.currentTimeMillis().toDouble())
            }

            sendEvent("barcodeScanned", payload)
        }
    }

    @ReactMethod
    fun startListening() {
        if (receiverRegistered) return

        reactContext.registerReceiver(
            scanReceiver,
            IntentFilter(scanAction),
            Context.RECEIVER_EXPORTED
        )

        receiverRegistered = true
    }

    @ReactMethod
    fun stopListening() {
        if (!receiverRegistered) return

        try {
            reactContext.unregisterReceiver(scanReceiver)
        } catch (_: Exception) {}

        receiverRegistered = false
    }
    
    /**
     * Required for React Native NativeEventEmitter
     */
    @ReactMethod
    fun addListener(eventName: String) {
        // No-op
    }

    /**
     * Required for React Native NativeEventEmitter
     */
    @ReactMethod
    fun removeListeners(count: Int) {
        // No-op
    }

    @ReactMethod
    fun triggerScan() {
        val intent = Intent().apply {
            action = "com.symbol.datawedge.api.ACTION"
            putExtra("com.symbol.datawedge.api.SOFT_SCAN_TRIGGER", "TOGGLE_SCANNING")
        }

        reactContext.sendBroadcast(intent)
    }

    private fun sendCommand(command: String, value: Any) {
        val intent = Intent().apply {
            action = "com.symbol.datawedge.api.ACTION"

            when (value) {
                is String -> putExtra(command, value)
                is Bundle -> putExtra(command, value)
            }
        }

        reactContext.sendBroadcast(intent)
    }

    private fun sendEvent(eventName: String, payload: WritableMap) {
        reactContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
            .emit(eventName, payload)
    }
    
    //
    @ReactMethod
    fun ensureProfile() {
        val profileName = "LST_MOBILE"

        sendCommand(
            "com.symbol.datawedge.api.CREATE_PROFILE",
            profileName
        )

        Thread.sleep(500)

        val barcodeConfig = Bundle().apply {
            putString("PLUGIN_NAME", "BARCODE")
            putString("RESET_CONFIG", "true")

            val props = Bundle().apply {
                putString("scanner_input_enabled", "true")

                // Auto-select internal scanner
                putString("scanner_selection", "auto")
                putString("scanner_selection_by_identifier", "AUTO")

                // Hardware trigger behavior
                putString("hardware_trigger_enabled", "true")
                putString("trigger_mode", "2") // 2 = HARD trigger

                // Disable Zebra's loud initial decode feedback
                putString("decode_audio_feedback_uri", "")
                putString("decode_haptic_feedback", "false")
                putString("decode_led_feedback", "false")

                // add in wake on trigger
                putString("trigger_wakeup_scan", "true");
            }

            putBundle("PARAM_LIST", props)
        }

        val intentConfig = Bundle().apply {
            putString("PLUGIN_NAME", "INTENT")
            putString("RESET_CONFIG", "true")

            val props = Bundle().apply {
                putString("intent_output_enabled", "true")
                putString("intent_action", scanAction)
                putString("intent_delivery", "2") // broadcast
                putString("intent_use_content_provider", "false")
            }

            putBundle("PARAM_LIST", props)
        }

        val keystrokeConfig = Bundle().apply {
            putString("PLUGIN_NAME", "KEYSTROKE")
            putString("RESET_CONFIG", "true")

            val props = Bundle().apply {
                putString("keystroke_output_enabled", "false")
            }

            putBundle("PARAM_LIST", props)
        }

        val profileConfig = Bundle().apply {
            putString("PROFILE_NAME", profileName)
            putString("PROFILE_ENABLED", "true")
            putString("CONFIG_MODE", "CREATE_IF_NOT_EXIST")

            putParcelableArrayList(
                "PLUGIN_CONFIG",
                arrayListOf(barcodeConfig, intentConfig, keystrokeConfig)
            )
        }

        sendCommand("com.symbol.datawedge.api.SET_CONFIG", profileConfig)

        val appConfig = Bundle().apply {
            putString("PACKAGE_NAME", reactContext.packageName)
            putStringArray("ACTIVITY_LIST", arrayOf("*"))
        }

        val associateConfig = Bundle().apply {
            putString("PROFILE_NAME", profileName)
            putString("CONFIG_MODE", "UPDATE")
            putParcelableArray("APP_LIST", arrayOf(appConfig))
        }

        sendCommand("com.symbol.datawedge.api.SET_CONFIG", associateConfig)

        // Runtime nudge: make sure scanner input is enabled for the active profile
        sendCommand(
            "com.symbol.datawedge.api.SCANNER_INPUT_PLUGIN",
            "ENABLE_PLUGIN"
        )
    }
}
`;

const packageCode = `package ${packageName}.scanner

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class ZebraScannerPackage : ReactPackage {

    override fun createNativeModules(
        reactContext: ReactApplicationContext
    ): List<NativeModule> {
        return listOf(ZebraScannerModule(reactContext))
    }

    override fun createViewManagers(
        reactContext: ReactApplicationContext
    ): List<ViewManager<*, *>> {
        return emptyList()
    }
}
`;

function patchMainApplication(mainApplicationPath) {
	let contents = fs.readFileSync(mainApplicationPath, "utf8");

	const importLine = `import ${packageName}.scanner.ZebraScannerPackage`;

	if (!contents.includes(importLine)) {
		contents = contents.replace(
			/import com\.facebook\.react\.PackageList/,
			`import com.facebook.react.PackageList\n${importLine}`,
		);
	}

	if (!contents.includes("add(ZebraScannerPackage())")) {
		contents = contents.replace(
			/PackageList\(this\)\.packages\.apply\s*\{/,
			`PackageList(this).packages.apply {\n          add(ZebraScannerPackage())`,
		);
	}

	fs.writeFileSync(mainApplicationPath, contents);
}

module.exports = function withZebraScanner(config) {
	return withDangerousMod(config, [
		"android",
		async (config) => {
			const androidRoot = config.modRequest.platformProjectRoot;

			const scannerDir = path.join(
				androidRoot,
				"app/src/main/java",
				packagePath,
				"scanner",
			);

			fs.mkdirSync(scannerDir, { recursive: true });

			fs.writeFileSync(
				path.join(scannerDir, "ZebraScannerModule.kt"),
				moduleCode,
			);

			fs.writeFileSync(
				path.join(scannerDir, "ZebraScannerPackage.kt"),
				packageCode,
			);

			const mainApplicationPath = path.join(
				androidRoot,
				"app/src/main/java",
				packagePath,
				"MainApplication.kt",
			);

			patchMainApplication(mainApplicationPath);

			return config;
		},
	]);
};
