package com.example.livingai.domain.ml

import android.graphics.Rect
import com.example.livingai.utils.Constants
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

class ArcoreDepthEstimator(
    private val params: ArcoreDepthParams = ArcoreDepthParams()
) : DistanceEstimator {

    data class ArcoreDepthParams(
        val targetDistanceMeters: Float = Constants.TARGET_DISTANCE_METERS,
        val strictToleranceMeters: Float = Constants.DISTANCE_TOLERANCE_METERS_STRICT,
        val relaxedToleranceMeters: Float = Constants.DISTANCE_TOLERANCE_METERS_RELAXED,
        val depthConfidenceMin: Float = Constants.DEPTH_CONFIDENCE_MIN,
        val minDepthSamples: Int = Constants.MIN_DEPTH_SAMPLES
    )

    override fun analyze(frame: FrameData, cameraInfo: CameraInfoData): DistanceState {

        val isTilted = abs(frame.imuPitchDegrees) > Constants.MAX_ACCEPTABLE_PITCH_DEGREES ||
                abs(frame.imuRollDegrees) > Constants.MAX_ACCEPTABLE_ROLL_DEGREES

        val isRotated = (frame.cameraRotationDegrees % 360) != 0
        val isOrientationCorrect = !isTilted && !isRotated

        val centered = checkCentered(frame, cameraInfo)

        val depthEstimate = sampleDepthMedian(frame)
        val fallbackEstimate = computeKnownDimensionEstimate(frame, cameraInfo)

        val fusedDistance = when {
            depthEstimate != null ->
                Constants.WEIGHT_ARCORE * depthEstimate +
                        Constants.WEIGHT_KNOWN_DIM * (fallbackEstimate ?: depthEstimate)

            fallbackEstimate != null -> fallbackEstimate
            else -> null
        }

        val (recommendation, ready, conf) =
            evaluateDistanceAndReadiness(fusedDistance, centered, isOrientationCorrect)

        return DistanceState(
            distanceMeters = fusedDistance,
            recommendation = recommendation,
            isCameraTilted = isTilted,
            isCameraRotated = isRotated,
            isOrientationCorrect = isOrientationCorrect,
            isObjectCentered = centered,
            readyToCapture = ready,
            confidenceScore = conf
        )
    }

    private fun sampleDepthMedian(frame: FrameData): Float? {
        val depth = frame.depthMapMeters ?: return null
        val w = frame.depthWidth
        val h = frame.depthHeight
        if (w <= 0 || h <= 0) return null

        val box = frame.segmentationBox ?: return null

        val left = max(0, box.left)
        val top = max(0, box.top)
        val right = min(w - 1, box.right)
        val bottom = min(h - 1, box.bottom)

        val conf = frame.depthConfidence
        val samples = ArrayList<Float>()

        for (y in top..bottom) {
            for (x in left..right) {
                val idx = y * w + x
                val d = depth[idx]
                if (d <= 0f || d.isNaN()) continue

                if (conf != null && conf[idx] < params.depthConfidenceMin)
                    continue

                samples.add(d)
            }
        }

        if (samples.size < params.minDepthSamples) return null
        samples.sort()
        return samples[samples.size / 2]
    }

    private fun computeKnownDimensionEstimate(
        frame: FrameData,
        cameraInfo: CameraInfoData
    ): Float? {
        val box = frame.segmentationBox ?: return null
        val hPixels = box.height().toFloat()
        if (hPixels <= 1f) return null

        val f = cameraInfo.focalLengthPixels
        val Hreal = Constants.DEFAULT_OBJECT_REAL_HEIGHT_METERS

        return (f * Hreal) / hPixels
    }

    private fun evaluateDistanceAndReadiness(
        distMeters: Float?,
        centered: Boolean,
        orientationOk: Boolean
    ): Triple<DistanceRecommendation, Boolean, Float> {

        if (distMeters == null)
            return Triple(DistanceRecommendation.DISTANCE_UNKNOWN, false, 0f)

        val diff = distMeters - params.targetDistanceMeters
        val absDiff = abs(diff)

        val withinStrict = absDiff <= params.strictToleranceMeters
        val withinRelaxed = absDiff <= params.relaxedToleranceMeters

        val recommendation = when {
            withinStrict -> DistanceRecommendation.AT_OPTIMAL_DISTANCE
            diff > 0f -> DistanceRecommendation.MOVE_CLOSER
            else -> DistanceRecommendation.MOVE_AWAY
        }

        val ready = withinRelaxed && centered && orientationOk

        val closenessScore = 1f - (absDiff / (params.relaxedToleranceMeters * 4f))
        val confidence = closenessScore * 0.8f +
                (if (centered && orientationOk) 0.2f else 0f)

        return Triple(recommendation, ready, confidence)
    }

    private fun checkCentered(frame: FrameData, cameraInfo: CameraInfoData): Boolean {
        val box = frame.segmentationBox ?: return false
        val imgW = cameraInfo.sensorWidthPx
        val imgH = cameraInfo.sensorHeightPx

        val cxObj = (box.left + box.right) / 2f
        val cyObj = (box.top + box.bottom) / 2f
        val cx = imgW / 2f
        val cy = imgH / 2f

        val dx = abs(cxObj - cx) / imgW
        val dy = abs(cyObj - cy) / imgH

        return dx <= Constants.CENTER_TOLERANCE_X_FRACTION &&
                dy <= Constants.CENTER_TOLERANCE_Y_FRACTION
    }
}
