import Lottie
import UIKit

// MARK: - RecordingLockView

/// Lock indicator shown above mic button during voice recording.
/// Telegram parity contract:
/// - Wrapper frame: 40x72
/// - Lock morph: y = 40 * lockness, height = 72 - 32 * lockness
/// - Lock icon shift: -11 * lockness, arrow shift: -39 * lockness
/// - Stop mode: clean 40x40 circle, delayed fade-in (0.56s)
final class RecordingLockView: UIView {

    private enum VisualState {
        case lock
        case stop
    }

    // MARK: - Layout Constants (Telegram exact)

    private let panelWidth: CGFloat = 40
    private let panelFullHeight: CGFloat = 72
    private let panelLockedHeight: CGFloat = 40
    private let verticalOffset: CGFloat = 122

    // MARK: - Subviews

    private let panelGlassView = TelegramGlassUIView(frame: .zero)
    private let panelBorderView = UIView()
    private let lockAnimationContainer = UIView()
    private let idleLockView = LottieAnimationView()
    private let lockingView = LottieAnimationView()
    private let lockFallbackGlyphView = UIImageView()
    private let lockArrowView = UIImageView()

    private let stopButton = UIButton(type: .custom)
    private let stopGlassView = TelegramGlassUIView(frame: .zero)
    private let stopBorderView = UIView()
    private let stopGlyphView = UIImageView()
    private var onStopTap: (() -> Void)?

    // MARK: - State

    private var currentLockness: CGFloat = 0
    private var visualState: VisualState = .lock

    private var usesComponentVisuals: Bool { false }

    private var isShowingStopButton: Bool {
        visualState == .stop
    }

    // MARK: - Init

    override init(frame: CGRect) {
        super.init(frame: frame)
        clipsToBounds = false
        isUserInteractionEnabled = true

        setupPanel()
        setupLockAnimations()
        setupArrow()
        setupStopButton()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) { fatalError() }

    // MARK: - Setup

    private func setupPanel() {
        panelGlassView.isUserInteractionEnabled = false
        panelGlassView.fixedCornerRadius = panelWidth / 2.0
        addSubview(panelGlassView)

        panelBorderView.isUserInteractionEnabled = false
        panelBorderView.backgroundColor = .clear
        panelBorderView.layer.cornerCurve = .continuous
        panelBorderView.layer.borderWidth = 1.0 / UIScreen.main.scale
        addSubview(panelBorderView)
    }

    private func setupLockAnimations() {
        lockAnimationContainer.isUserInteractionEnabled = false
        addSubview(lockAnimationContainer)

        // Use Main Thread renderer — Core Animation renderer doesn't support Fill Color tinting
        let mainThreadConfig = LottieConfiguration(renderingEngine: .mainThread)

        if let animation = LottieAnimation.named(VoiceRecordingLottieAsset.lockWait.rawValue) {
            idleLockView.animation = animation
            idleLockView.configuration = mainThreadConfig
        }
        idleLockView.backgroundBehavior = .pauseAndRestore
        idleLockView.loopMode = .autoReverse
        idleLockView.contentMode = .scaleAspectFit
        idleLockView.isUserInteractionEnabled = false
        lockAnimationContainer.addSubview(idleLockView)

        if let animation = LottieAnimation.named(VoiceRecordingLottieAsset.lock.rawValue) {
            lockingView.animation = animation
            lockingView.configuration = mainThreadConfig
        }
        lockingView.backgroundBehavior = .pauseAndRestore
        lockingView.loopMode = .playOnce
        lockingView.contentMode = .scaleAspectFit
        lockingView.currentProgress = 0
        lockingView.isHidden = true
        lockingView.isUserInteractionEnabled = false
        lockAnimationContainer.addSubview(lockingView)

        let lockConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .regular)
        lockFallbackGlyphView.image = UIImage(systemName: "lock", withConfiguration: lockConfig)
        lockFallbackGlyphView.contentMode = .center
        lockFallbackGlyphView.isHidden = true
        lockFallbackGlyphView.isUserInteractionEnabled = false
        lockAnimationContainer.addSubview(lockFallbackGlyphView)
    }

    private func setupArrow() {
        // Arrow is part of the Lottie animation — no separate UIImageView needed.
        lockArrowView.isHidden = true
    }

    private func setupStopButton() {
        stopButton.isHidden = true
        stopButton.alpha = 0
        stopButton.clipsToBounds = false
        stopButton.backgroundColor = .clear
        stopButton.isUserInteractionEnabled = false
        stopButton.accessibilityIdentifier = "voice.recording.stop"
        stopButton.accessibilityLabel = "Stop recording"
        stopButton.accessibilityHint = "Stops voice recording and opens preview."
        stopButton.addTarget(self, action: #selector(stopTapped), for: .touchUpInside)

        stopGlassView.isUserInteractionEnabled = false
        stopGlassView.fixedCornerRadius = 20
        stopButton.addSubview(stopGlassView)

        stopBorderView.isUserInteractionEnabled = false
        stopBorderView.backgroundColor = .clear
        stopBorderView.layer.cornerCurve = .continuous
        stopBorderView.layer.borderWidth = 1.0 / UIScreen.main.scale
        stopBorderView.isHidden = true
        stopButton.addSubview(stopBorderView)

        stopGlyphView.isUserInteractionEnabled = false
        stopGlyphView.contentMode = .center
        stopGlyphView.image = VoiceRecordingAssets.image(.pause, templated: true)
            ?? Self.makeStopGlyphFallback()
        stopButton.addSubview(stopGlyphView)

        addSubview(stopButton)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updatePanelGeometry()
    }

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        guard previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle else { return }
        updateAppearance()
    }

    private func updatePanelGeometry() {
        let clampedLockness = max(0, min(1, currentLockness))
        let panelHeight: CGFloat
        let panelY: CGFloat

        switch visualState {
        case .lock:
            panelHeight = panelFullHeight - (panelFullHeight - panelLockedHeight) * clampedLockness
            panelY = 40.0 * clampedLockness
        case .stop:
            panelHeight = panelLockedHeight
            panelY = 32.0
        }

        let panelFrame = CGRect(x: 0, y: panelY, width: panelWidth, height: panelHeight)
        panelGlassView.frame = panelFrame
        panelGlassView.fixedCornerRadius = min(panelWidth, panelHeight) / 2.0
        panelGlassView.applyCornerRadius()

        panelBorderView.frame = panelFrame
        panelBorderView.layer.cornerRadius = min(panelWidth, panelHeight) / 2.0

        lockAnimationContainer.frame = CGRect(x: 0, y: 6.0, width: 40.0, height: 60.0)
        idleLockView.frame = lockAnimationContainer.bounds
        lockingView.frame = lockAnimationContainer.bounds
        lockFallbackGlyphView.frame = CGRect(x: 8.0, y: 8.0, width: 24.0, height: 24.0)

        let arrowSize = lockArrowView.image?.size ?? CGSize(width: 18, height: 9)
        lockArrowView.frame = CGRect(
            x: floor((panelWidth - arrowSize.width) / 2.0),
            y: 54.0,
            width: arrowSize.width,
            height: arrowSize.height
        )

        stopButton.frame = CGRect(x: 0, y: 32.0, width: 40.0, height: 40.0)
        stopGlassView.frame = stopButton.bounds
        stopGlassView.fixedCornerRadius = stopButton.bounds.height / 2.0
        stopGlassView.applyCornerRadius()

        stopBorderView.frame = stopButton.bounds
        stopBorderView.layer.cornerRadius = stopButton.bounds.height / 2.0
        stopGlyphView.frame = stopButton.bounds
    }

    // MARK: - Present

    /// Position above anchor (mic button) and animate in with spring.
    func present(anchorCenter: CGPoint, in parent: UIView) {
        frame = CGRect(
            x: floor(anchorCenter.x - panelWidth / 2.0),
            y: floor(anchorCenter.y - verticalOffset - panelFullHeight / 2.0),
            width: panelWidth,
            height: panelFullHeight
        )

        currentLockness = 0
        visualState = .lock
        onStopTap = nil

        lockAnimationContainer.transform = .identity
        lockAnimationContainer.alpha = 1
        lockAnimationContainer.isHidden = false
        lockAnimationContainer.isUserInteractionEnabled = false

        lockArrowView.transform = .identity
        lockArrowView.alpha = 1
        lockArrowView.isHidden = false

        stopButton.isHidden = true
        stopButton.alpha = 0
        stopButton.transform = .identity
        stopButton.isUserInteractionEnabled = false

        lockingView.isHidden = true
        lockingView.currentProgress = 0
        lockFallbackGlyphView.isHidden = true
        if idleLockView.animation != nil {
            idleLockView.isHidden = false
            if !idleLockView.isAnimationPlaying {
                idleLockView.play()
            }
        } else {
            idleLockView.isHidden = true
            lockFallbackGlyphView.isHidden = false
        }

        updatePanelGeometry()
        updateAppearance()

        parent.addSubview(self)

        transform = CGAffineTransform(translationX: 0, y: 100)
        alpha = 0
        UIView.animate(
            withDuration: 0.5,
            delay: 0,
            usingSpringWithDamping: 0.55,
            initialSpringVelocity: 0,
            options: [.beginFromCurrentState]
        ) {
            self.transform = .identity
            self.alpha = 1
        }
    }

    // MARK: - Lockness Update

    /// Update lock progress (0 = idle, 1 = locked).
    /// Telegram: lockPanelWrapperView.transform = full drag Y translation
    func updateLockness(_ lockness: CGFloat, dragOffsetY: CGFloat = 0) {
        guard visualState == .lock else { return }

        currentLockness = max(0, min(1, lockness))
        // Move entire pill upward with finger (distanceY is negative when swiping up)
        transform = CGAffineTransform(translationX: 0, y: dragOffsetY)
        updatePanelGeometry()

        if currentLockness > 0 {
            if idleLockView.isAnimationPlaying {
                idleLockView.stop()
            }
            idleLockView.isHidden = true

            if lockingView.animation != nil {
                lockingView.isHidden = false
                lockingView.currentProgress = currentLockness
                lockFallbackGlyphView.isHidden = true
            } else {
                lockingView.isHidden = true
                lockFallbackGlyphView.isHidden = false
                lockFallbackGlyphView.alpha = 0.85 + 0.15 * currentLockness
                lockFallbackGlyphView.transform = CGAffineTransform(
                    scaleX: 0.94 + 0.06 * currentLockness,
                    y: 0.94 + 0.06 * currentLockness
                )
            }
        } else {
            lockingView.isHidden = true
            lockFallbackGlyphView.transform = .identity

            if idleLockView.animation != nil {
                idleLockView.isHidden = false
                lockFallbackGlyphView.isHidden = true
                if !idleLockView.isAnimationPlaying {
                    idleLockView.play()
                }
            } else {
                idleLockView.isHidden = true
                lockFallbackGlyphView.isHidden = false
            }
        }

        lockAnimationContainer.transform = CGAffineTransform(translationX: 0, y: -11.0 * currentLockness)
        lockArrowView.transform = CGAffineTransform(translationX: 0, y: -39.0 * currentLockness)
        lockArrowView.alpha = max(0, 1 - currentLockness * 1.6)
    }

    func showStopButton(onTap: @escaping () -> Void) {
        onStopTap = onTap
        visualState = .stop
        currentLockness = 1.0

        if idleLockView.isAnimationPlaying {
            idleLockView.stop()
        }
        idleLockView.isHidden = true
        lockingView.isHidden = true
        lockFallbackGlyphView.isHidden = true

        lockAnimationContainer.layer.removeAllAnimations()
        lockAnimationContainer.alpha = 0
        lockAnimationContainer.isHidden = true
        lockAnimationContainer.isUserInteractionEnabled = false

        lockArrowView.layer.removeAllAnimations()
        lockArrowView.alpha = 0
        lockArrowView.isHidden = true
        lockArrowView.isUserInteractionEnabled = false

        updatePanelGeometry()
        updateAppearance()

        // Hide panel glass so it doesn't stack under stopGlassView (double glass fix)
        panelGlassView.alpha = 0
        panelBorderView.alpha = 0

        stopButton.isHidden = false
        stopButton.isUserInteractionEnabled = true
        stopButton.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
        stopButton.alpha = 0
        stopButton.layer.zPosition = 100
        bringSubviewToFront(stopButton)

        // Telegram: animate view back to fixed position (reset drag offset)
        // Lock panel wrapper slides to final locked Y, then stop button fades in
        UIView.animate(withDuration: 0.2, delay: 0, options: [.curveEaseInOut]) {
            self.transform = .identity
        }

        UIView.animate(withDuration: 0.25, delay: 0.56, options: [.curveEaseOut]) {
            self.stopButton.alpha = 1
            self.stopButton.transform = .identity
        }
    }

    // MARK: - Dismiss

    func dismiss() {
        visualState = .lock
        if idleLockView.isAnimationPlaying {
            idleLockView.stop()
        }

        UIView.animate(withDuration: 0.18) {
            self.alpha = 0
            self.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
        } completion: { _ in
            self.removeFromSuperview()
        }
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        if isShowingStopButton {
            return stopButton.frame.insetBy(dx: -10, dy: -10).contains(point)
        }
        return bounds.insetBy(dx: -6, dy: -6).contains(point)
    }

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        guard self.point(inside: point, with: event), !isHidden, alpha > 0.01 else { return nil }
        if isShowingStopButton {
            let stopPoint = convert(point, to: stopButton)
            if stopButton.point(inside: stopPoint, with: event) {
                return stopButton
            }
            return nil
        }
        return super.hitTest(point, with: event)
    }

    private func updateAppearance() {
        let isDark = traitCollection.userInterfaceStyle == .dark
        let iconColor: UIColor
        let borderColor: UIColor

        if usesComponentVisuals {
            iconColor = isDark ? UIColor(white: 0.95, alpha: 0.92) : UIColor(white: 0.05, alpha: 0.92)
            borderColor = isDark ? UIColor(white: 1.0, alpha: 0.22) : UIColor(white: 0.0, alpha: 0.14)
        } else {
            iconColor = isDark ? UIColor.white : UIColor(white: 0.20, alpha: 1.0)
            borderColor = isDark ? UIColor(white: 1.0, alpha: 0.18) : UIColor(white: 0.0, alpha: 0.14)
        }

        panelBorderView.layer.borderColor = borderColor.cgColor
        panelBorderView.isHidden = usesComponentVisuals
        panelBorderView.alpha = visualState == .lock ? 1.0 : 0.0

        stopBorderView.layer.borderColor = borderColor.cgColor
        stopGlyphView.tintColor = iconColor
        lockFallbackGlyphView.tintColor = iconColor

        // Tint Lottie animations (Telegram: allKeypaths predicate "Color")
        // Note: "Fill Color" is NOT supported by Core Animation renderer — only Color, Stroke Color
        var r: CGFloat = 0; var g: CGFloat = 0; var b: CGFloat = 0; var a: CGFloat = 0
        iconColor.getRed(&r, green: &g, blue: &b, alpha: &a)
        let lottieColor = LottieColor(r: Double(r), g: Double(g), b: Double(b), a: Double(a))
        let colorProvider = ColorValueProvider(lottieColor)
        for keypath in ["**.Color", "**.Stroke Color", "**.Fill Color"] {
            let kp = AnimationKeypath(keypath: keypath)
            idleLockView.setValueProvider(colorProvider, keypath: kp)
            lockingView.setValueProvider(colorProvider, keypath: kp)
        }
    }

    // MARK: - Stop Action

    @objc private func stopTapped() {
        stopButton.isUserInteractionEnabled = false
        UIView.animate(withDuration: 0.2) {
            self.stopButton.alpha = 0
        }
        onStopTap?()
    }

    // MARK: - Images

    private static func makeStopGlyphFallback() -> UIImage? {
        let size = CGSize(width: 30, height: 30)
        let renderer = UIGraphicsImageRenderer(size: size)
        return renderer.image { ctx in
            ctx.cgContext.setFillColor(UIColor.black.cgColor)

            let leftBar = UIBezierPath(
                roundedRect: CGRect(x: 7.5, y: 8.0, width: 5.0, height: 14.0),
                cornerRadius: 1.0
            )
            leftBar.fill()

            let rightBar = UIBezierPath(
                roundedRect: CGRect(x: 17.5, y: 8.0, width: 5.0, height: 14.0),
                cornerRadius: 1.0
            )
            rightBar.fill()
        }
    }
}
