package ru.forbion.f7cloud.mobile.qr

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.camera.core.Camera
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import com.google.zxing.BarcodeFormat
import com.google.zxing.BinaryBitmap
import com.google.zxing.DecodeHintType
import com.google.zxing.MultiFormatReader
import com.google.zxing.common.HybridBinarizer
import ru.forbion.f7cloud.core.designsystem.F7Colors
import ru.forbion.f7cloud.core.designsystem.F7Theme
import ru.forbion.f7cloud.core.network.LoginFlowClient
import ru.forbion.f7cloud.mobile.R
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean

class F7QrScannerActivity : ComponentActivity() {

    private val finishing = AtomicBoolean(false)
    private val analysisExecutor = Executors.newSingleThreadExecutor()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            F7Theme {
                QrScannerScreen(
                    onClose = { finish() },
                    onStableResult = ::finishWithStableResult,
                )
            }
        }
    }

    override fun onDestroy() {
        analysisExecutor.shutdown()
        super.onDestroy()
    }

    private fun finishWithStableResult(payload: String) {
        if (!finishing.compareAndSet(false, true)) return
        setResult(
            RESULT_OK,
            Intent().putExtra(RESULT_EXTRA, payload),
        )
        finish()
    }

    companion object {
        const val RESULT_EXTRA = "ru.forbion.f7cloud.mobile.qr_scan_result"
        private const val REQUIRED_STABLE_READS = 5

        fun intent(context: Context): Intent =
            Intent(context, F7QrScannerActivity::class.java)
    }

    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    private fun QrScannerScreen(
        onClose: () -> Unit,
        onStableResult: (String) -> Unit,
    ) {
        val context = LocalContext.current
        val idleHint = stringResource(R.string.qr_scan_hint_idle)
        var hint by remember(idleHint) { mutableStateOf(idleHint) }
        var stableCount by remember { mutableIntStateOf(0) }
        var torchEnabled by remember { mutableStateOf(false) }
        var camera by remember { mutableStateOf<Camera?>(null) }
        val previewView = remember { PreviewView(context).apply { implementationMode = PreviewView.ImplementationMode.COMPATIBLE } }

        val reader = remember {
            MultiFormatReader().apply {
                setHints(
                    mapOf(
                        DecodeHintType.POSSIBLE_FORMATS to listOf(BarcodeFormat.QR_CODE),
                        DecodeHintType.CHARACTER_SET to "UTF-8",
                    ),
                )
            }
        }

        val mainExecutor = remember { ContextCompat.getMainExecutor(context) }
        var lastPayload by remember { mutableStateOf<String?>(null) }

        fun onDecode(text: String?) {
            if (finishing.get()) return
            if (text.isNullOrBlank()) {
                lastPayload = null
                stableCount = 0
                hint = context.getString(R.string.qr_scan_hint_idle)
                return
            }
            val trimmed = text.trim()
            if (!LoginFlowClient.isCompleteQrPayload(trimmed)) {
                lastPayload = null
                stableCount = 0
                hint = context.getString(R.string.qr_scan_hint_align)
                return
            }
            if (trimmed == lastPayload) {
                stableCount += 1
            } else {
                lastPayload = trimmed
                stableCount = 1
            }
            hint = if (stableCount >= REQUIRED_STABLE_READS) {
                context.getString(R.string.qr_scan_hint_done)
            } else {
                context.getString(R.string.qr_scan_hint_progress, stableCount, REQUIRED_STABLE_READS)
            }
            if (stableCount >= REQUIRED_STABLE_READS) {
                onStableResult(trimmed)
            }
        }

        DisposableEffect(previewView) {
            val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
            cameraProviderFuture.addListener({
                val cameraProvider = cameraProviderFuture.get()
                val preview = Preview.Builder().build().also {
                    it.surfaceProvider = previewView.surfaceProvider
                }
                val analysis = ImageAnalysis.Builder()
                    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                    .build()
                analysis.setAnalyzer(analysisExecutor) { imageProxy ->
                    val decoded = decodeQr(reader, imageProxy)
                    imageProxy.close()
                    mainExecutor.execute { onDecode(decoded) }
                }
                runCatching {
                    cameraProvider.unbindAll()
                    camera = cameraProvider.bindToLifecycle(
                        this@F7QrScannerActivity,
                        CameraSelector.DEFAULT_BACK_CAMERA,
                        preview,
                        analysis,
                    )
                }
            }, mainExecutor)
            onDispose {
                runCatching { cameraProviderFuture.get().unbindAll() }
            }
        }

        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text(stringResource(R.string.qr_scan_title)) },
                    navigationIcon = {
                        IconButton(onClick = onClose) {
                            Icon(
                                imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                                contentDescription = stringResource(R.string.qr_scan_close),
                            )
                        }
                    },
                    actions = {
                        IconButton(
                            onClick = {
                                val next = !torchEnabled
                                camera?.cameraControl?.enableTorch(next)
                                torchEnabled = next
                            },
                        ) {
                            Text(
                                text = if (torchEnabled) {
                                    stringResource(R.string.qr_scan_torch_off)
                                } else {
                                    stringResource(R.string.qr_scan_torch_on)
                                },
                                style = MaterialTheme.typography.labelLarge,
                                color = Color.White,
                            )
                        }
                    },
                    colors = TopAppBarDefaults.topAppBarColors(
                        containerColor = Color.Black.copy(alpha = 0.55f),
                        titleContentColor = Color.White,
                        navigationIconContentColor = Color.White,
                        actionIconContentColor = Color.White,
                    ),
                )
            },
            containerColor = Color.Black,
        ) { padding ->
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(padding),
            ) {
                AndroidView(
                    modifier = Modifier.fillMaxSize(),
                    factory = { previewView },
                )

                QrFinderOverlay(modifier = Modifier.fillMaxSize())

                Text(
                    text = hint,
                    modifier = Modifier
                        .align(Alignment.BottomCenter)
                        .padding(horizontal = 24.dp, vertical = 32.dp),
                    style = MaterialTheme.typography.bodyLarge,
                    color = Color.White,
                    textAlign = TextAlign.Center,
                )
            }
        }
    }

    @Composable
    private fun QrFinderOverlay(modifier: Modifier = Modifier) {
        val frameColor = F7Colors.Primary
        Canvas(modifier = modifier) {
            val frameSize = minOf(size.width, size.height) * 0.68f
            val left = (size.width - frameSize) / 2f
            val top = (size.height - frameSize) / 2f
            val frameRect = Rect(Offset(left, top), Size(frameSize, frameSize))

            val overlayPath = Path().apply {
                fillType = PathFillType.EvenOdd
                addRect(Rect(Offset.Zero, size))
                addRoundRect(RoundRect(frameRect, CornerRadius(16f, 16f)))
            }
            drawPath(
                path = overlayPath,
                color = Color.Black.copy(alpha = 0.55f),
            )
            drawRoundRect(
                color = frameColor,
                topLeft = frameRect.topLeft,
                size = frameRect.size,
                cornerRadius = CornerRadius(16f, 16f),
                style = Stroke(width = 4f),
                blendMode = BlendMode.SrcOver,
            )
        }
    }

    private fun decodeQr(reader: MultiFormatReader, imageProxy: ImageProxy): String? {
        if (imageProxy.format != android.graphics.ImageFormat.YUV_420_888) return null
        val yBuffer = imageProxy.planes[0].buffer
        val ySize = yBuffer.remaining()
        val yuv = ByteArray(ySize)
        yBuffer.get(yuv)

        val width = imageProxy.width
        val height = imageProxy.height
        val source = com.google.zxing.PlanarYUVLuminanceSource(
            yuv,
            width,
            height,
            0,
            0,
            width,
            height,
            false,
        )
        return runCatching {
            reader.decodeWithState(BinaryBitmap(HybridBinarizer(source))).text
        }.getOrNull()?.also {
            reader.reset()
        } ?: run {
            reader.reset()
            null
        }
    }
}
