package com.example.livingai.domain.ml

import com.example.livingai.utils.Constants
import kotlin.math.abs
import kotlin.math.min

class KnownDimensionEstimator(
    private val params: KnownDimensionParams = KnownDimensionParams()
) : DistanceEstimator {

    data class KnownDimensionParams(
        val knownObjectHeightMeters: Float = Constants.DEFAULT_OBJECT_REAL_HEIGHT_METERS,
        val targetDistanceMeters: Float = Constants.TARGET_DISTANCE_METERS,
        val strictToleranceMeters: Float = Constants.DISTANCE_TOLERANCE_METERS_STRICT,
        val relaxedToleranceMeters: Float = Constants.DISTANCE_TOLERANCE_METERS_RELAXED
    )

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

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

        val rotated = (frame.cameraRotationDegrees % 360) != 0
        val orientationOk = !tilted && !rotated
        val centered = checkCentered(frame, cameraInfo)

        val distEstimate = computeDistance(frame, cameraInfo)

        val (recommendation, ready, conf) =
            evaluateDistanceAndReadiness(distEstimate, centered, orientationOk)

        return DistanceState(
            distanceMeters = distEstimate,
            recommendation = recommendation,
            isCameraTilted = tilted,
            isCameraRotated = rotated,
            isOrientationCorrect = orientationOk,
            isObjectCentered = centered,
            readyToCapture = ready,
            confidenceScore = conf
        )
    }

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

        val f = cameraInfo.focalLengthPixels
        return (f * params.knownObjectHeightMeters) / hPx
    }

    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 - min(1f, absDiff / (params.relaxedToleranceMeters * 4f))
        val conf = closenessScore * 0.9f +
                (if (centered && orientationOk) 0.1f else 0f)

        return Triple(recommendation, ready, conf)
    }

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

        val objCx = (box.left + box.right) / 2f
        val objCy = (box.top + box.bottom) / 2f

        val cx = imgW / 2f
        val cy = imgH / 2f

        val dx = abs(objCx - cx) / imgW
        val dy = abs(objCy - cy) / imgH

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