import CoreGraphics
import Foundation

enum VoiceRecordingReleaseDecision: String {
    case finish
    case cancel
    case lock
}

enum VoiceRecordingParityConstants {
    static let holdThreshold: TimeInterval = 0.19
    static let cancelDistanceThreshold: CGFloat = -150
    static let cancelHapticThreshold: CGFloat = -100
    static let lockDistanceThreshold: CGFloat = -110
    static let lockHapticThreshold: CGFloat = -60
    static let velocityGate: CGFloat = -400
    static let preHoldCancelDistance: CGFloat = 10
    static let micHitInsetX: CGFloat = -10

    static let locknessDivisor: CGFloat = 105
    static let dragNormalizeDivisor: CGFloat = 300
    static let cancelTransformThreshold: CGFloat = 8

    static let sendAccessibilityHitSize: CGFloat = 120
    static let minVoiceDuration: TimeInterval = 0.5
    static let minFreeDiskBytes: Int64 = 8 * 1024 * 1024

    static func minTrimDuration(duration: TimeInterval, waveformWidth: CGFloat) -> TimeInterval {
        max(1.0, 56.0 * duration / max(waveformWidth, 1))
    }
}

enum VoiceRecordingParityMath {
    static func dominantAxisDistances(distanceX: CGFloat, distanceY: CGFloat) -> (CGFloat, CGFloat) {
        if abs(distanceX) > abs(distanceY) {
            return (distanceX, 0)
        } else {
            return (0, distanceY)
        }
    }

    static func releaseDecision(
        velocityX: CGFloat,
        velocityY: CGFloat,
        distanceX: CGFloat,
        distanceY: CGFloat
    ) -> VoiceRecordingReleaseDecision {
        if velocityX < VoiceRecordingParityConstants.velocityGate ||
            distanceX < VoiceRecordingParityConstants.cancelHapticThreshold {
            return .cancel
        }
        if velocityY < VoiceRecordingParityConstants.velocityGate ||
            distanceY < VoiceRecordingParityConstants.lockHapticThreshold {
            return .lock
        }
        return .finish
    }

    static func lockness(distanceY: CGFloat) -> CGFloat {
        min(1, max(0, abs(distanceY) / VoiceRecordingParityConstants.locknessDivisor))
    }

    static func normalizedDrag(distance: CGFloat) -> CGFloat {
        max(0, min(1, abs(distance) / VoiceRecordingParityConstants.dragNormalizeDivisor))
    }

    static func shouldApplyCancelTransform(_ translation: CGFloat) -> Bool {
        abs(translation) > VoiceRecordingParityConstants.cancelTransformThreshold
    }

    static func shouldDiscard(duration: TimeInterval) -> Bool {
        duration < VoiceRecordingParityConstants.minVoiceDuration
    }

    static func clampTrimRange(_ trimRange: ClosedRange<TimeInterval>, duration: TimeInterval) -> ClosedRange<TimeInterval> {
        let lower = max(0, min(trimRange.lowerBound, duration))
        let upper = max(lower, min(trimRange.upperBound, duration))
        return lower...upper
    }

    static func waveformSliceRange(
        sampleCount: Int,
        totalDuration: TimeInterval,
        trimRange: ClosedRange<TimeInterval>
    ) -> Range<Int>? {
        guard sampleCount > 0, totalDuration > 0 else { return nil }
        let startIndex = max(0, Int(floor((trimRange.lowerBound / totalDuration) * Double(sampleCount))))
        let endIndex = min(sampleCount, Int(ceil((trimRange.upperBound / totalDuration) * Double(sampleCount))))
        guard startIndex < endIndex else { return nil }
        return startIndex..<endIndex
    }
}
