package com.myidsdk import android.content.Context import com.facebook.react.bridge.ReadableMap import uz.myid.android.sdk.capture.MyIdConfig import uz.myid.android.sdk.capture.model.MyIdCameraResolution import uz.myid.android.sdk.capture.model.MyIdCameraSelector import uz.myid.android.sdk.capture.model.MyIdCameraShape import uz.myid.android.sdk.capture.model.MyIdEntryType import uz.myid.android.sdk.capture.model.MyIdEnvironment import uz.myid.android.sdk.capture.model.MyIdImageFormat import uz.myid.android.sdk.capture.model.MyIdLocale import uz.myid.android.sdk.capture.model.MyIdOrganizationDetails import uz.myid.android.sdk.capture.model.MyIdResidency import uz.myid.android.sdk.capture.model.MyIdScreenOrientation /** * Translates the JS config payload into a [MyIdConfig]. * * A setter is only called when its key is present, so an omitted option — or an * unrecognised enum string — leaves the SDK default in place. */ internal object MyidSdkConfigMapper { fun build(config: ReadableMap, context: Context): MyIdConfig { val builder = MyIdConfig.Builder(config.requireString("sessionId")) .withClientHash( config.requireString("clientHash"), config.requireString("clientHashId") ) config.optInt("minAge")?.let { builder.withMinAge(it) } config.optDouble("distance")?.let { builder.withDistance(it.toFloat()) } config.optBoolean("withSoundGuides")?.let { builder.withSoundGuides(it) } config.optBoolean("showErrorScreen")?.let { builder.withErrorScreen(it) } config.optString("huaweiAppId")?.let { builder.withHuaweiAppId(it) } config.optEnum("environment")?.let { when (it) { "DEBUG" -> MyIdEnvironment.Debug "PRODUCTION" -> MyIdEnvironment.Production else -> null } }?.let(builder::withEnvironment) config.optEnum("entryType")?.let { when (it) { "IDENTIFICATION" -> MyIdEntryType.Identification "VIDEO_IDENTIFICATION" -> MyIdEntryType.VideoIdentification "FACE_DETECTION" -> MyIdEntryType.FaceDetection else -> null } }?.let(builder::withEntryType) config.optEnum("residency")?.let { when (it) { "USER_DEFINED" -> MyIdResidency.UserDefined "RESIDENT" -> MyIdResidency.Resident "NON_RESIDENT" -> MyIdResidency.NonResident else -> null } }?.let(builder::withResidency) config.optEnum("locale")?.let { when (it) { "UZBEK" -> MyIdLocale.Uzbek "UZBEK_CYRILLIC" -> MyIdLocale.UzbekCyrillic "KARAKALPAK" -> MyIdLocale.Karakalpak "TAJIK" -> MyIdLocale.Tajik "ENGLISH" -> MyIdLocale.English "RUSSIAN" -> MyIdLocale.Russian else -> null } }?.let(builder::withLocale) config.optEnum("cameraShape")?.let { when (it) { "CIRCLE" -> MyIdCameraShape.Circle "ELLIPSE" -> MyIdCameraShape.Ellipse else -> null } }?.let(builder::withCameraShape) config.optEnum("cameraSelector")?.let { when (it) { "FRONT" -> MyIdCameraSelector.Front "BACK" -> MyIdCameraSelector.Back else -> null } }?.let(builder::withCameraSelector) config.optEnum("cameraResolution")?.let { when (it) { "LOW" -> MyIdCameraResolution.Low "HIGH" -> MyIdCameraResolution.High else -> null } }?.let(builder::withCameraResolution) config.optEnum("imageFormat")?.let { when (it) { "JPEG" -> MyIdImageFormat.JPEG "PNG" -> MyIdImageFormat.PNG else -> null } }?.let(builder::withImageFormat) config.optEnum("screenOrientation")?.let { when (it) { "PORTRAIT" -> MyIdScreenOrientation.Portrait "LANDSCAPE" -> MyIdScreenOrientation.Landscape "FULL" -> MyIdScreenOrientation.Full else -> null } }?.let(builder::withScreenOrientation) // withOrganizationDetails copies the object verbatim, so an empty one would // blank out the SDK default carrying MyID's call-centre number. config.optMap("organizationDetails") ?.takeIf { it.hasKey("phone") || it.hasKey("logo") } ?.let { details -> builder.withOrganizationDetails( MyIdOrganizationDetails( phoneNumber = details.optString("phone"), logo = details.optString("logo")?.let { context.drawableIdOrNull(it) } ) ) } return builder.build() } private fun ReadableMap.requireString(key: String): String = optString(key) ?: throw IllegalArgumentException("Missing required config field: $key") private fun ReadableMap.optString(key: String): String? = if (hasKey(key) && !isNull(key)) getString(key) else null /** Normalise case so 'debug' works like 'DEBUG'. */ private fun ReadableMap.optEnum(key: String): String? = optString(key)?.uppercase() private fun ReadableMap.optInt(key: String): Int? = if (hasKey(key) && !isNull(key)) getDouble(key).toInt() else null private fun ReadableMap.optDouble(key: String): Double? = if (hasKey(key) && !isNull(key)) getDouble(key) else null private fun ReadableMap.optBoolean(key: String): Boolean? = if (hasKey(key) && !isNull(key)) getBoolean(key) else null private fun ReadableMap.optMap(key: String): ReadableMap? = if (hasKey(key) && !isNull(key)) getMap(key) else null /** A missing asset degrades to no logo. */ private fun Context.drawableIdOrNull(name: String): Int? = resources.getIdentifier(name, "drawable", packageName).takeIf { it != 0 } }