import ObjectiveC
import QuartzCore
import UIKit

// MARK: - VoiceRecordingOverlay

/// Telegram-exact recording overlay. Values from audit of:
/// - TGModernConversationInputMicButton.m (lines 11-13, 909-938)
/// - ChatTextInputAudioRecordingOverlayButton.swift (lines 8-173)
///
/// Z-order (back→front): outerCircle → innerCircle → micIcon
/// Inner circle: 110pt, #0088FF, opaque
/// Outer circle: 160pt, #0088FF alpha 0.2, scales with audio
/// Mic icon: Telegram `InputMicRecordingOverlay`, top-most layer
final class VoiceRecordingOverlay {

    // Telegram exact (lines 11-13 of TGModernConversationInputMicButton.m)
    private let innerDiameter: CGFloat = 110
    private let outerDiameter: CGFloat = 160
    private var outerMinScale: CGFloat { innerDiameter / outerDiameter } // 0.6875

    // Telegram exact: UIColor(rgb: 0x0088ff)
    private let telegramBlue = UIColor(red: 0, green: 136/255.0, blue: 1.0, alpha: 1)

    // MARK: - Views

    private let containerView = UIView()
    private let outerCircle = UIView()
    private let innerCircle = UIView()
    private let iconView = UIImageView()

    // MARK: - Display Link

    private var displayLink: CADisplayLink?
    private var displayLinkTarget: DisplayLinkTarget?
    private var animationStartTime: Double = 0
    private var currentLevel: CGFloat = 0
    private var inputLevel: CGFloat = 0
    private var dragDistanceX: CGFloat = 0
    private var dragDistanceY: CGFloat = 0

    private var isLocked = false
    private var onTapStop: (() -> Void)?

    // MARK: - Init

    init() {
        containerView.isUserInteractionEnabled = false

        // Outer circle: 160pt, #0088FF alpha 0.2
        outerCircle.backgroundColor = telegramBlue.withAlphaComponent(0.2)
        outerCircle.bounds = CGRect(origin: .zero, size: CGSize(width: outerDiameter, height: outerDiameter))
        outerCircle.layer.cornerRadius = outerDiameter / 2

        // Inner circle: 110pt, #0088FF opaque
        innerCircle.backgroundColor = telegramBlue
        innerCircle.bounds = CGRect(origin: .zero, size: CGSize(width: innerDiameter, height: innerDiameter))
        innerCircle.layer.cornerRadius = innerDiameter / 2

        configureIcon()

        // Z-order: outer (back) → inner → icon (front)
        containerView.addSubview(outerCircle)
        containerView.addSubview(innerCircle)
        containerView.addSubview(iconView)
    }

    deinit {
        displayLink?.invalidate()
        containerView.removeFromSuperview()
    }

    private func configureIcon() {
        iconView.image = VoiceRecordingAssets.image(.inputMicOverlay, templated: true)
        iconView.tintColor = .white
        iconView.contentMode = .center
        iconView.isUserInteractionEnabled = false
    }

    // MARK: - Present (Telegram exact: spring damping 0.55, duration 0.5s)

    func present(anchorView: UIView, in window: UIWindow) {
        guard let superview = anchorView.superview else { return }

        // Telegram: centerOffset = (0, -1 + screenPixel)
        var center = superview.convert(anchorView.center, to: window)
        center.y += (-1.0 + (1.0 / UIScreen.main.scale))

        containerView.bounds = CGRect(origin: .zero, size: CGSize(width: outerDiameter, height: outerDiameter))
        containerView.center = center
        containerView.transform = .identity
        containerView.alpha = 1

        let mid = CGPoint(x: outerDiameter / 2, y: outerDiameter / 2)
        outerCircle.center = mid
        innerCircle.center = mid
        let iconSize = iconView.image?.size ?? CGSize(width: 30, height: 30)
        iconView.frame = CGRect(
            x: floor(mid.x - iconSize.width / 2.0),
            y: floor(mid.y - iconSize.height / 2.0),
            width: iconSize.width,
            height: iconSize.height
        )

        window.addSubview(containerView)

        // Start state: scale 0.2, alpha 0.2 (Telegram exact)
        innerCircle.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
        innerCircle.alpha = 0.2
        outerCircle.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
        outerCircle.alpha = 0.2
        iconView.alpha = 0.2
        iconView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)

        // Alpha fade: 0.15s (Telegram exact)
        UIView.animate(withDuration: 0.15) {
            self.innerCircle.alpha = 1
            self.outerCircle.alpha = 1
            self.iconView.alpha = 1
        }

        // Spring scale: damping 0.55, duration 0.5s (Telegram exact)
        // Inner → 1.0
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.55, initialSpringVelocity: 0, options: .beginFromCurrentState) {
            self.innerCircle.transform = .identity
            self.iconView.transform = .identity
        }
        // Outer → outerMinScale (0.6875)
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.55, initialSpringVelocity: 0, options: .beginFromCurrentState) {
            self.applyCurrentTransforms()
        }

        animationStartTime = CACurrentMediaTime()
        startDisplayLink()
    }

    // MARK: - Lock Transition (mic overlay → send icon, tappable)

    /// Transition to locked state: icon morphs to Telegram `RecordSendIcon`.
    /// Telegram: TGModernConversationInputMicButton.m line 616-693
    func transitionToLocked(onTapStop: @escaping () -> Void) {
        isLocked = true
        self.onTapStop = onTapStop
        containerView.isUserInteractionEnabled = true

        // Add tap gesture via helper target
        let tapTarget = TapTarget { onTapStop() }
        let tap = UITapGestureRecognizer(target: tapTarget, action: #selector(TapTarget.tapped))
        containerView.addGestureRecognizer(tap)
        // Retain the target via associated object
        objc_setAssociatedObject(containerView, "tapTarget", tapTarget, .OBJC_ASSOCIATION_RETAIN)

        // Reset drag transforms: scale back to 1.0, position to center
        UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
            self.innerCircle.transform = .identity
            self.outerCircle.transform = CGAffineTransform(
                scaleX: self.outerMinScale, y: self.outerMinScale
            )
            self.iconView.transform = .identity
        }

        dragDistanceX = 0
        dragDistanceY = 0

        // Transition icon: mic overlay → send icon.
        transitionToSendIcon()
    }

    /// Telegram-style icon transition using snapshot shrink + new icon grow.
    private func transitionToSendIcon() {
        guard let sendImage = VoiceRecordingAssets.image(.recordSendIcon, templated: true) else {
            return
        }
        let snapshot = iconView.snapshotView(afterScreenUpdates: false)
        snapshot?.frame = iconView.frame
        if let snapshot {
            containerView.addSubview(snapshot)
        }

        iconView.image = sendImage
        iconView.tintColor = .white
        iconView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
        iconView.alpha = 0

        UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
            self.iconView.transform = .identity
            self.iconView.alpha = 1
            snapshot?.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
            snapshot?.alpha = 0
        } completion: { _ in
            snapshot?.removeFromSuperview()
        }
    }

    // MARK: - Dismiss (Telegram exact: 0.18s, scale→0.2, alpha→0)

    func dismiss() {
        stopDisplayLink()

        // Capture containerView strongly — overlay may be deallocated before the
        // delayed cleanup fires (ComposerView sets recordingOverlay = nil immediately).
        let container = containerView

        UIView.animate(withDuration: 0.18, animations: {
            self.innerCircle.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
            self.innerCircle.alpha = 0
            self.outerCircle.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
            self.outerCircle.alpha = 0
            self.iconView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
            self.iconView.alpha = 0
        })

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
            container.removeFromSuperview()
        }
    }

    /// Cancel-specific dismiss: leftward translation matching cancel drag direction.
    func dismissCancel() {
        stopDisplayLink()

        let container = containerView

        UIView.animate(withDuration: 0.18, animations: {
            self.innerCircle.transform = CGAffineTransform(translationX: -80, y: 0)
                .scaledBy(x: 0.2, y: 0.2)
            self.innerCircle.alpha = 0
            self.outerCircle.transform = CGAffineTransform(translationX: -80, y: 0)
                .scaledBy(x: 0.2, y: 0.2)
            self.outerCircle.alpha = 0
            self.iconView.transform = CGAffineTransform(translationX: -80, y: 0)
                .scaledBy(x: 0.2, y: 0.2)
            self.iconView.alpha = 0
        })

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
            container.removeFromSuperview()
        }
    }

    // MARK: - Audio Level

    func addMicLevel(_ level: CGFloat) {
        inputLevel = level
    }

    // MARK: - Drag Transforms (Telegram: displayLinkUpdate lines 921-936)

    /// Apply drag-based transforms to overlay circles.
    /// distanceX: negative = sliding left (cancel), distanceY: negative = sliding up (lock)
    /// Telegram exact from TGModernConversationInputMicButton.m
    func applyDragTransform(distanceX: CGFloat, distanceY: CGFloat) {
        guard CACurrentMediaTime() > animationStartTime else { return }
        dragDistanceX = min(0, distanceX)
        dragDistanceY = min(0, distanceY)
        applyCurrentTransforms()
    }

    // MARK: - Display Link (Telegram: displayLinkEvent, 0.8/0.2 smoothing)

    private func startDisplayLink() {
        guard displayLink == nil else { return }
        let target = DisplayLinkTarget { [weak self] in self?.tick() }
        let link = CADisplayLink(target: target, selector: #selector(DisplayLinkTarget.tick))
        link.preferredFrameRateRange = CAFrameRateRange(minimum: 24, maximum: 30, preferred: 30)
        link.add(to: .main, forMode: .common)
        displayLink = link
        displayLinkTarget = target
    }

    private func stopDisplayLink() {
        displayLink?.invalidate()
        displayLink = nil
        displayLinkTarget = nil
    }

    private func tick() {
        // Telegram: wait 0.5s for spring to settle before reacting to audio
        guard CACurrentMediaTime() > animationStartTime + 0.5 else { return }

        // Telegram exact: TGModernConversationInputMicButton.m line 916 (0.9/0.1)
        currentLevel = currentLevel * 0.9 + inputLevel * 0.1

        applyCurrentTransforms()
    }

    private func applyCurrentTransforms() {
        let valueX = max(0, min(1, abs(dragDistanceX) / 300))
        let innerScale = max(0.4, min(1.0, 1.0 - valueX))
        let translatedX = dragDistanceX * innerScale

        let translation = CGAffineTransform(translationX: translatedX, y: dragDistanceY)
        let outerScale = outerMinScale + currentLevel * (1.0 - outerMinScale)
        outerCircle.transform = translation.scaledBy(x: outerScale, y: outerScale)

        let innerTransform = translation.scaledBy(x: innerScale, y: innerScale)
        innerCircle.transform = innerTransform

        let mid = CGPoint(x: outerDiameter / 2, y: outerDiameter / 2)
        iconView.center = CGPoint(
            x: mid.x + translatedX,
            y: mid.y + dragDistanceY
        )
        iconView.transform = CGAffineTransform(scaleX: innerScale, y: innerScale)
    }
}

// MARK: - DisplayLinkTarget

private final class DisplayLinkTarget: NSObject {
    let callback: () -> Void
    init(_ callback: @escaping () -> Void) { self.callback = callback }
    @objc func tick() { callback() }
}

// MARK: - TapTarget

private final class TapTarget: NSObject {
    let callback: () -> Void
    init(_ callback: @escaping () -> Void) { self.callback = callback }
    @objc func tapped() { callback() }
}
