package com.performancecoachlab.posedetection.camera

import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle.Companion.Fill
import androidx.compose.ui.graphics.PaintingStyle.Companion.Stroke
import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.rotate
import androidx.compose.ui.text.TextMeasurer
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import com.performancecoachlab.posedetection.recording.AnalysisResult
import com.performancecoachlab.posedetection.skeleton.Skeleton

fun ImageBitmap.drawSkeleton(
    inskeleton: Skeleton?, rotation: Float = 0f, mirrored: Boolean = false
): ImageBitmap {
    val skeleton = inskeleton?.let {
        if (mirrored) {
            it.mirror(this.height.toFloat())
        } else {
            it
        }
    }
    also {
        val bitmap = ImageBitmap(it.width, it.height)
        val canvas = Canvas(bitmap).apply {
            if (rotation != 0f) rotate(rotation, it.width.toFloat() / 2f, it.height.toFloat() / 2f)
        }
        val drawScope = CanvasDrawScope()
        val size = Size(it.width.toFloat(), it.height.toFloat())

        // Calculate scaling factor based on skeleton size
        val skeletonSize = skeleton?.joints()?.let { joints ->
            val minX = joints.minOfOrNull { joint -> joint.x } ?: 0f
            val maxX = joints.maxOfOrNull { joint -> joint.x } ?: 0f
            val minY = joints.minOfOrNull { joint -> joint.y } ?: 0f
            val maxY = joints.maxOfOrNull { joint -> joint.y } ?: 0f
            kotlin.math.min(maxX - minX, maxY - minY)
        } ?: 1f
        val scaleFactor = skeletonSize / kotlin.math.min(it.width, it.height)

        drawScope.draw(
            Density(1f),
            LayoutDirection.Ltr,
            canvas,
            size,
        ) {
            drawImage(it)

            skeleton?.apply {
                val baseStrokeWidth = 10f
                val scaledStrokeWidth = baseStrokeWidth * scaleFactor

                val paintWhite = Paint().apply {
                    color = Color.White
                    strokeWidth = scaledStrokeWidth
                    style = Stroke
                }
                val paintBlue = Paint().apply {
                    color = Color.Blue
                    strokeWidth = 0.8f * scaledStrokeWidth
                    style = Fill
                }
                bones().forEach { line ->
                    drawLine(
                        color = paintWhite.color, start = androidx.compose.ui.geometry.Offset(
                            line.first.x, line.first.y
                        ), end = androidx.compose.ui.geometry.Offset(
                            line.second.x, line.second.y
                        ), strokeWidth = paintWhite.strokeWidth, blendMode = BlendMode.Softlight
                    )
                    drawLine(
                        color = paintBlue.color, start = androidx.compose.ui.geometry.Offset(
                            line.first.x, line.first.y
                        ), end = androidx.compose.ui.geometry.Offset(
                            line.second.x, line.second.y
                        ), strokeWidth = paintBlue.strokeWidth, blendMode = BlendMode.Color
                    )
                }

                joints().forEach { joint ->
                    drawCircle(
                        brush = Brush.radialGradient(
                            colors = listOf(Color.Blue, Color.Transparent),
                            center = androidx.compose.ui.geometry.Offset(joint.x, joint.y),
                            radius = 1.2f * scaledStrokeWidth
                        ),
                        radius = 1.2f * scaledStrokeWidth,
                        center = androidx.compose.ui.geometry.Offset(joint.x, joint.y)
                    )
                }
            }
        }
        return bitmap
    }
}

fun ImageBitmap.drawResults(
    skeleton: Skeleton?, drawableObjects: List<DrawableObject>,
    textMeasurer: TextMeasurer? = null
): ImageBitmap {
    also { it ->
        val bitmap = ImageBitmap(it.width, it.height)
        val canvas = Canvas(bitmap)
        val drawScope = CanvasDrawScope()
        val size = Size(it.width.toFloat(), it.height.toFloat())

        // Calculate scaling factor based on skeleton size
        val skeletonSize = skeleton?.joints()?.let { joints ->
            val minX = joints.minOfOrNull { joint -> joint.x } ?: 0f
            val maxX = joints.maxOfOrNull { joint -> joint.x } ?: 0f
            val minY = joints.minOfOrNull { joint -> joint.y } ?: 0f
            val maxY = joints.maxOfOrNull { joint -> joint.y } ?: 0f
            kotlin.math.max(maxX - minX, maxY - minY)
        } ?: 1f
        val minDime = kotlin.math.min(it.width, it.height).toFloat()
        val scaleFactor = skeletonSize / minDime
        val baseStrokeWidth = 30f
        val scaledStrokeWidth = (baseStrokeWidth * scaleFactor).coerceIn(2f, minDime / 50f)

        drawScope.draw(
            Density(1f),
            LayoutDirection.Ltr,
            canvas,
            size,
        ) {
            drawImage(it)
            for (drawableObject in drawableObjects) {
                when (drawableObject.shape) {
                    DrawableShape.OVAL -> drawOval(
                        color = drawableObject.colour,
                        topLeft = androidx.compose.ui.geometry.Offset(
                            drawableObject.obj.boundingBox.left, drawableObject.obj.boundingBox.top
                        ),
                        size = Size(
                            drawableObject.obj.boundingBox.width,
                            drawableObject.obj.boundingBox.height
                        ),
                        style = drawableObject.style
                    )

                    DrawableShape.RECTANGLE -> drawRect(
                        color = drawableObject.colour,
                        topLeft = androidx.compose.ui.geometry.Offset(
                            drawableObject.obj.boundingBox.left, drawableObject.obj.boundingBox.top
                        ),
                        size = Size(
                            drawableObject.obj.boundingBox.width,
                            drawableObject.obj.boundingBox.height
                        ),
                        style = drawableObject.style
                    )

                    DrawableShape.LABEL -> {
                        // Defer actual text rendering to platform-specific implementation
                        drawLabelTextPlatform(
                            drawableObject = drawableObject,
                            textMeasurer = textMeasurer
                        )
                    }
                }
            }
            skeleton?.apply {
                val paintWhite = Paint().apply {
                    color = Color.White
                    strokeWidth = scaledStrokeWidth
                    style = Stroke
                }
                val paintBlue = Paint().apply {
                    color = Color.Blue
                    strokeWidth = 0.8f * scaledStrokeWidth
                    style = Fill
                }
                bones().forEach { line ->
                    drawLine(
                        color = paintWhite.color, start = androidx.compose.ui.geometry.Offset(
                            line.first.x, line.first.y
                        ), end = androidx.compose.ui.geometry.Offset(
                            line.second.x, line.second.y
                        ), strokeWidth = paintWhite.strokeWidth, blendMode = BlendMode.Softlight
                    )
                    drawLine(
                        color = paintBlue.color, start = androidx.compose.ui.geometry.Offset(
                            line.first.x, line.first.y
                        ), end = androidx.compose.ui.geometry.Offset(
                            line.second.x, line.second.y
                        ), strokeWidth = paintBlue.strokeWidth, blendMode = BlendMode.Color
                    )
                }

                joints().forEach { joint ->
                    drawCircle(
                        brush = Brush.radialGradient(
                            colors = listOf(Color.Blue, Color.Transparent),
                            center = androidx.compose.ui.geometry.Offset(joint.x, joint.y),
                            radius = 1.2f * scaledStrokeWidth
                        ),
                        radius = 1.2f * scaledStrokeWidth,
                        center = androidx.compose.ui.geometry.Offset(joint.x, joint.y)
                    )
                }
            }
        }
        return bitmap
    }
}

fun ImageBitmap.drawAnalysisResults(
    analysisResults: AnalysisResult, rotation: Float = 0f, mirrored: Boolean = false
): ImageBitmap {
    val skeleton = analysisResults.skeleton?.let {
        if (mirrored) {
            it.mirror(this.height.toFloat())
        } else {
            it
        }
    }
    also {
        val bitmap = ImageBitmap(it.width, it.height)
        val canvas = Canvas(bitmap).apply {
            if (rotation != 0f) rotate(
                rotation, it.width.toFloat() / 2f, it.height.toFloat() / 2f
            )
        }
        val drawScope = CanvasDrawScope()
        val size = Size(it.width.toFloat(), it.height.toFloat())

        // Compute scale factors to map detection coordinates to target bitmap.
        // Use independent X/Y scaling — handles the case where the skeleton's
        // coordinate space has a different aspect ratio than the target frame
        // (e.g., iOS portrait analysis vs landscape extracted video).
        val skelScaleX = if (skeleton != null && skeleton.width > 0f)
            it.width.toFloat() / skeleton.width else 1f
        val skelScaleY = if (skeleton != null && skeleton.height > 0f)
            it.height.toFloat() / skeleton.height else 1f
        val skelOffsetX = 0f
        val skelOffsetY = 0f

        // Calculate scaling factor based on skeleton size (after mapping to target)
        val skeletonSize = skeleton?.joints()?.let { joints ->
            val minX = joints.minOfOrNull { joint -> joint.x * skelScaleX } ?: 0f
            val maxX = joints.maxOfOrNull { joint -> joint.x * skelScaleX } ?: 0f
            val minY = joints.minOfOrNull { joint -> joint.y * skelScaleY } ?: 0f
            val maxY = joints.maxOfOrNull { joint -> joint.y * skelScaleY } ?: 0f
            kotlin.math.max(maxX - minX, maxY - minY)
        } ?: 1f
        val minDime = kotlin.math.min(it.width, it.height).toFloat()
        val scaleFactor = skeletonSize / minDime
        val baseStrokeWidth = 30f
        val scaledStrokeWidth = (baseStrokeWidth * scaleFactor).coerceIn(2f, minDime / 50f)

        drawScope.draw(
            Density(1f),
            LayoutDirection.Ltr,
            canvas,
            size,
        ) {
            drawImage(it)
            analysisResults.objects.forEach { analysisObject ->
                val oScaleX = if (analysisObject.frameSize.width > 0)
                    it.width.toFloat() / analysisObject.frameSize.width else 1f
                val oScaleY = if (analysisObject.frameSize.height > 0)
                    it.height.toFloat() / analysisObject.frameSize.height else 1f
                drawRect(
                    color = Color.Red, topLeft = androidx.compose.ui.geometry.Offset(
                        analysisObject.boundingBox.left * oScaleX,
                        analysisObject.boundingBox.top * oScaleY
                    ), size = Size(
                        analysisObject.boundingBox.width * oScaleX,
                        analysisObject.boundingBox.height * oScaleY
                    ), style = Stroke(scaledStrokeWidth)
                )
            }
            skeleton?.apply {

                val paintWhite = Paint().apply {
                    color = Color.White
                    strokeWidth = scaledStrokeWidth
                    style = Stroke
                }
                val paintBlue = Paint().apply {
                    color = Color.Blue
                    strokeWidth = 0.8f * scaledStrokeWidth
                    style = Fill
                }
                bones().forEach { line ->
                    drawLine(
                        color = paintWhite.color, start = androidx.compose.ui.geometry.Offset(
                            line.first.x * skelScaleX + skelOffsetX, line.first.y * skelScaleY + skelOffsetY
                        ), end = androidx.compose.ui.geometry.Offset(
                            line.second.x * skelScaleX + skelOffsetX, line.second.y * skelScaleY + skelOffsetY
                        ), strokeWidth = paintWhite.strokeWidth, blendMode = BlendMode.Softlight
                    )
                    drawLine(
                        color = paintBlue.color, start = androidx.compose.ui.geometry.Offset(
                            line.first.x * skelScaleX + skelOffsetX, line.first.y * skelScaleY + skelOffsetY
                        ), end = androidx.compose.ui.geometry.Offset(
                            line.second.x * skelScaleX + skelOffsetX, line.second.y * skelScaleY + skelOffsetY
                        ), strokeWidth = paintBlue.strokeWidth, blendMode = BlendMode.Color
                    )
                }

                joints().forEach { joint ->
                    val jx = joint.x * skelScaleX + skelOffsetX
                    val jy = joint.y * skelScaleY + skelOffsetY
                    drawCircle(
                        brush = Brush.radialGradient(
                            colors = listOf(Color.Blue, Color.Transparent),
                            center = androidx.compose.ui.geometry.Offset(jx, jy),
                            radius = 1.2f * scaledStrokeWidth
                        ),
                        radius = 1.2f * scaledStrokeWidth,
                        center = androidx.compose.ui.geometry.Offset(jx, jy)
                    )
                }
            }
        }
        return bitmap
    }
}

fun Rect.normalize(): Rect {
    return Rect(
        left = minOf(this.left, this.right),
        top = minOf(this.top, this.bottom),
        right = maxOf(this.left, this.right),
        bottom = maxOf(this.top, this.bottom)
    )
}

// Platform-specific label text drawing
expect fun DrawScope.drawLabelTextPlatform(
    drawableObject: DrawableObject,
    textMeasurer: TextMeasurer?
)

expect enum class PlatformType {
    ANDROID, IOS;

    companion object {
        fun getCurrentPlatform(): PlatformType
    }
}