package tk.zwander.common.compose.components

import android.content.SharedPreferences
import android.os.Build
import android.view.WindowManager
import androidx.compose.foundation.Image
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import tk.zwander.common.compose.util.rememberBooleanPreferenceState
import tk.zwander.common.compose.util.rememberIntPreferenceState
import tk.zwander.common.compose.util.rememberPreferenceState
import tk.zwander.common.drawable.BackgroundBlurDrawableCompat
import tk.zwander.common.util.BaseDelegate
import tk.zwander.common.util.logUtils
import tk.zwander.common.util.prefManager
import java.util.function.Consumer

@Composable
fun BaseDelegate.BaseViewModel<*, *>.BlurView(
    blurKey: Pair<String, SharedPreferences>,
    blurAmountKey: Pair<String, SharedPreferences>,
    modifier: Modifier = Modifier,
    cornerRadiusKey: String? = null,
) {
    val density = LocalDensity.current
    val view = LocalView.current
    val context = LocalContext.current
    var currentDrawable by remember {
        mutableStateOf<BackgroundBlurDrawableCompat?>(null)
    }
    var crossBlurEnabled by remember {
        mutableStateOf(
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && wm?.isCrossWindowBlurEnabled == true,
        )
    }

    val shouldBlur by rememberBooleanPreferenceState(
        key = blurKey.first,
        preferences = blurKey.second,
    )
    val blurAmount by rememberIntPreferenceState(
        key = blurAmountKey.first,
        preferences = blurKey.second,
        defaultValue = 100,
    )
    val cornerRadiusPx by if (cornerRadiusKey != null) {
        rememberPreferenceState(
            key = cornerRadiusKey,
            value = { context.prefManager.getInt(cornerRadiusKey, 20) / 10f },
        )
    } else {
        remember { mutableFloatStateOf(0f) }
    }

    DisposableEffect(null) {
        val crossBlurEnabledListener = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            Consumer<Boolean> {
                crossBlurEnabled = it
                if (!it) {
                    currentDrawable = null
                }
            }
        } else {
            null
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            crossBlurEnabledListener?.let { wm?.addCrossWindowBlurEnabledListener(it) }
        }

        onDispose {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                crossBlurEnabledListener?.let { wm?.removeCrossWindowBlurEnabledListener(it) }
            }
            currentDrawable = null
        }
    }

    LaunchedEffect(
        crossBlurEnabled,
        shouldBlur,
        blurAmount,
        cornerRadiusPx,
    ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            var newBlurDrawable: BackgroundBlurDrawableCompat? = currentDrawable

            if (blurAmount > 0 &&
                shouldBlur &&
                crossBlurEnabled &&
                view.rootView.viewRootImpl?.isHardwareEnabled == true
            ) {
                if (newBlurDrawable == null) {
                    context.logUtils.debugLog("Creating BackgroundBlurDrawableCompat.", null)
                    newBlurDrawable = BackgroundBlurDrawableCompat(view.rootView.viewRootImpl)
                }
            } else {
                newBlurDrawable = null
            }

            newBlurDrawable?.setBlurRadius(blurAmount)
            newBlurDrawable?.setCornerRadius(
                with(density) {
                    cornerRadiusPx.dp.toPx()
                },
            )

            context.logUtils.debugLog("Setting blur drawable $newBlurDrawable on target view with current background ${currentDrawable}.", null)

            currentDrawable = newBlurDrawable
        } else {
            val f = try {
                params::class.java.getDeclaredField("samsungFlags")
            } catch (e: Exception) {
                context.logUtils.debugLog("Error getting Samsung flags field", e)
                return@LaunchedEffect
            }

            if (shouldBlur) {
                params.flags = params.flags or WindowManager.LayoutParams.FLAG_DIM_BEHIND

                f?.set(params, f.get(params) as Int or 64)
                params.dimAmount = blurAmount / 1000f
            } else {
                params.flags = params.flags and WindowManager.LayoutParams.FLAG_DIM_BEHIND.inv()

                f?.set(params, f.get(params) as Int and 64.inv())
                params.dimAmount = 0.0f
            }

            updateWindow()
        }
    }

    if (shouldBlur && currentDrawable != null) {
        Image(
            painter = rememberDrawablePainter(currentDrawable),
            contentDescription = null,
            modifier = modifier,
        )
    }
}
