package com.performancecoachlab.posedetection.camera

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.rememberTextMeasurer
import com.performancecoachlab.posedetection.custom.CustomObjectRespository
import com.performancecoachlab.posedetection.custom.ObjectModel
import com.performancecoachlab.posedetection.recording.AnalysisObject
import com.performancecoachlab.posedetection.skeleton.SkeletonRepository
import kotlinx.cinterop.BetaInteropApi
import kotlinx.coroutines.delay
import kotlin.native.runtime.NativeRuntimeApi

@OptIn(NativeRuntimeApi::class, BetaInteropApi::class)
@Composable
actual fun CameraView(
    skeletonRepository: SkeletonRepository,
    customObjectRepository: CustomObjectRespository,
    detectMode: DetectMode,
    drawSkeleton: Boolean,
    objectModel: ObjectModel?,
    drawObjects: ((List<AnalysisObject>) -> List<DrawableObject>)?,
    modifier: Modifier,
    frontCamera: Boolean,
    useUltraWide: Boolean,
    previewFillMode: PreviewFillMode,
    recordingId: String?,
    focusArea: Rect?,
    controller: CameraViewController?,
    onRecordToggled: (Boolean) -> Unit,
    onVideoSaved: (String, String) -> Unit,
) {
    val cameraEngine = remember { mutableStateOf<CameraEngine?>(null) }
    val frameListener = remember { FrameRepository() }
    val frameBitmap by frameListener.frameFlow.collectAsState()
    var lastRecordingState by remember { mutableStateOf(false) }
    var idMap by remember { mutableStateOf<Map<String, String>>(emptyMap()) }
    val textMeasurer = rememberTextMeasurer()
    LaunchedEffect(detectMode) {
        cameraEngine.value?.setDetectMode(detectMode)
    }

    // Ensure lens selection is deterministic and recomposition-safe.
    LaunchedEffect(frontCamera, useUltraWide) {
        cameraEngine.value?.setUseFrontCamera(frontCamera)
        cameraEngine.value?.setUseUltraWideSafe(useUltraWide)
    }

    val recordingDone = { path: String ->
        val id = idMap.entries.firstOrNull { it.value == path }?.key
        if (id != null) {
            onVideoSaved(id, path)
            idMap = idMap - id
        }
    }
    LaunchedEffect(cameraEngine.value, recordingId) {
        cameraEngine.value?.apply {
            addSkeletonRepository(skeletonRepository)
            addCustomObjectRepository(customObjectRepository)
            addCameraViewController(controller)
            addFrameListener(frameListener)
            setOnVideoSavedCallback(recordingDone)
            setDrawOptions(
                drawSkeleton = drawSkeleton,
                drawObjects = drawObjects,
            )
            setTextMeasurer(textMeasurer)

            if (recordingId != null) {
                (if (lastRecordingState) splitRecording()
                else startRecording())?.also {
                    idMap = idMap + (recordingId to it)
                    lastRecordingState = true
                }
            } else {
                if (lastRecordingState) {
                    stopRecording()
                }
                lastRecordingState = false
            }
        }
    }
    LaunchedEffect(focusArea) {
        cameraEngine.value?.setFocusArea(focusArea)
    }
    LaunchedEffect(objectModel) {
        cameraEngine.value?.setObjectModel(objectModel)
    }
    LaunchedEffect(Unit) {
        delay(1000L)
        cameraEngine.value?.setObjectModel(objectModel)
        cameraEngine.value?.setFocusArea(focusArea)
        cameraEngine.value?.setDetectMode(detectMode)
    }
    LaunchedEffect(previewFillMode) {
        cameraEngine.value?.setPreviewFillMode(previewFillMode)
    }
    Box(modifier = Modifier.fillMaxSize()) {
        CameraPreview(
            modifier = Modifier.fillMaxSize(),
            onCameraControllerReady = { engine ->
                cameraEngine.value = engine
                // Apply immediately as well for the first composition.
                engine.setUseFrontCamera(frontCamera)
                engine.setUseUltraWideSafe(useUltraWide)
                engine.setPreviewFillMode(previewFillMode)
            },
        )
        if (drawSkeleton || drawObjects != null) {
            // IMPORTANT: Do not force GC from composition; it can cause visible flicker/jitter.
            // (GC.collect() is still performed in the capture pipeline.)
            frameBitmap?.also {
                Image(
                    bitmap = it,
                    contentDescription = "video frame",
                    modifier = Modifier.fillMaxSize(),
                    contentScale = ContentScale.Crop,
                )
            }
        }

    }
}

// Temporary forwarder to ensure call-sites compile.
private fun CameraEngine.setUseUltraWideSafe(useUltraWide: Boolean) {
    this.setUseUltraWide(useUltraWide)
}
