import QuartzCore
import UIKit
import Lottie

// MARK: - VoiceRecordingPanelDelegate

@MainActor
protocol VoiceRecordingPanelDelegate: AnyObject {
    func recordingPanelDidTapCancel(_ panel: VoiceRecordingPanel)
}

// MARK: - VoiceRecordingPanel

/// Recording bar shown inside the composer during voice recording.
/// Telegram parity from ChatTextInputPanelNode.swift audit:
/// - Red dot: 10×10, #FF2D55, keyframe pulsing 0.5s
/// - Timer: 15pt mono, X=34pt from left
/// - "< Slide to cancel": centered, 14pt, panelControlColor
/// - Jiggle animation: 6pt, 1.0s easeInOut, infinite
final class VoiceRecordingPanel: UIView {

    weak var delegate: VoiceRecordingPanelDelegate?

    // MARK: - Subviews

    // Glass background (matches input container style)
    private let glassBackground = TelegramGlassUIView(frame: .zero)

    // Red dot (10×10, #FF2D55)
    private let redDot = UIView()

    // Timer (15pt monospaced)
    private let timerLabel = UILabel()

    // Cancel indicator container (arrow + "Slide to cancel")
    private let cancelContainer = UIView()
    private let arrowIcon = UIImageView()
    private let slideLabel = UILabel()

    // Cancel button (shown in locked state, 17pt)
    private let cancelButton = UIButton(type: .system)

    // MARK: - State

    private(set) var isDisplayingCancel = false
    private var isEntryAnimationComplete = false

    // MARK: - Telegram-exact layout constants

    private let dotX: CGFloat = 14    // Left margin for red dot
    private let timerX: CGFloat = 38   // Timer X position
    private let dotSize: CGFloat = 10
    private let timerMinWidth: CGFloat = 72
    private let arrowLabelGap: CGFloat = 6

    private var panelControlColor: UIColor {
        UIColor { traits in
            traits.userInterfaceStyle == .dark ? UIColor.white : UIColor.black
        }
    }

    private var panelControlAccentColor: UIColor {
        UIColor { traits in
            traits.userInterfaceStyle == .dark
                ? UIColor.white
                : UIColor(red: 0, green: 136 / 255.0, blue: 1.0, alpha: 1.0)
        }
    }

    private var recordingDotColor: UIColor {
        UIColor { traits in
            traits.userInterfaceStyle == .dark
                ? UIColor(red: 0xEB / 255.0, green: 0x55 / 255.0, blue: 0x45 / 255.0, alpha: 1.0)
                : UIColor(red: 0xED / 255.0, green: 0x25 / 255.0, blue: 0x21 / 255.0, alpha: 1.0)
        }
    }

    // MARK: - Init

    override init(frame: CGRect) {
        super.init(frame: frame)
        clipsToBounds = true
        layer.cornerRadius = 21
        layer.cornerCurve = .continuous
        setupSubviews()
    }

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

    // MARK: - Setup

    private func setupSubviews() {
        // Glass background (matches input container style)
        glassBackground.fixedCornerRadius = 21
        glassBackground.isUserInteractionEnabled = false
        addSubview(glassBackground)

        // Red dot: 10×10, theme-aware Telegram recording color.
        redDot.backgroundColor = recordingDotColor
        redDot.layer.cornerRadius = dotSize / 2
        addSubview(redDot)

        // Timer: 15pt monospaced rounded (Telegram: Font.with(size: 15, design: .camera))
        if let descriptor = UIFont.systemFont(ofSize: 15, weight: .regular)
            .fontDescriptor.withDesign(.rounded) {
            timerLabel.font = UIFont(descriptor: descriptor, size: 15)
        } else {
            timerLabel.font = .monospacedDigitSystemFont(ofSize: 15, weight: .regular)
        }
        timerLabel.textColor = panelControlColor
        timerLabel.text = "0:00"
        timerLabel.lineBreakMode = .byClipping
        timerLabel.isAccessibilityElement = true
        timerLabel.accessibilityIdentifier = "voice.recording.timer"
        addSubview(timerLabel)

        // Arrow: Telegram asset "AudioRecordingCancelArrow" (arrowleft.svg, 9×18pt)
        arrowIcon.image = VoiceRecordingAssets.image(.cancelArrow, templated: true)
        arrowIcon.tintColor = panelControlColor
        arrowIcon.contentMode = .center
        cancelContainer.addSubview(arrowIcon)

        // "Slide to cancel": 14pt regular, panelControlColor = #FFFFFF (dark theme)
        slideLabel.font = .systemFont(ofSize: 14, weight: .regular)
        slideLabel.textColor = panelControlColor
        slideLabel.text = "Slide to cancel"
        cancelContainer.addSubview(slideLabel)
        cancelContainer.isAccessibilityElement = true
        cancelContainer.accessibilityLabel = "Slide left to cancel recording"
        cancelContainer.accessibilityIdentifier = "voice.recording.slideToCancel"
        addSubview(cancelContainer)

        // Cancel button (for locked state): 17pt
        cancelButton.setTitle("Cancel", for: .normal)
        cancelButton.setTitleColor(panelControlAccentColor, for: .normal)
        cancelButton.titleLabel?.font = .systemFont(ofSize: 17, weight: .regular)
        cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
        cancelButton.isAccessibilityElement = true
        cancelButton.accessibilityLabel = "Cancel recording"
        cancelButton.accessibilityHint = "Discards the current recording."
        cancelButton.accessibilityIdentifier = "voice.recording.cancel"
        cancelButton.alpha = 0
        addSubview(cancelButton)

        updateThemeColors()
    }

    // MARK: - Layout

    override func layoutSubviews() {
        super.layoutSubviews()
        let h = bounds.height
        let w = bounds.width

        // Glass background
        glassBackground.frame = bounds
        glassBackground.applyCornerRadius()

        // Red dot: 10×10, centered vertically with timer
        let timerSize = timerLabel.sizeThatFits(CGSize(width: 100, height: h))
        let timerY = floor((h - timerSize.height) / 2)

        redDot.frame = CGRect(
            x: dotX,
            y: timerY + floor((timerSize.height - dotSize) / 2),
            width: dotSize,
            height: dotSize
        )

        // Timer: at X=34
        let timerWidth = max(timerMinWidth, timerSize.width + 4)
        timerLabel.frame = CGRect(x: timerX, y: timerY, width: timerWidth, height: timerSize.height)

        // Cancel indicator: centered with slight right offset to balance timer weight
        let labelSize = slideLabel.sizeThatFits(CGSize(width: 200, height: h))
        let arrowW: CGFloat = 9   // Telegram SVG: 9pt wide
        let arrowH: CGFloat = 18  // Telegram SVG: 18pt tall
        let totalCancelW = arrowW + 12 + labelSize.width
        let cancelRightShift: CGFloat = 16
        let cancelX = floor((w - totalCancelW) / 2) + cancelRightShift

        cancelContainer.frame = CGRect(x: cancelX, y: 0, width: totalCancelW, height: h)
        arrowIcon.frame = CGRect(x: 0, y: floor((h - arrowH) / 2), width: arrowW, height: arrowH)
        slideLabel.frame = CGRect(
            x: arrowW + arrowLabelGap,
            y: 1 + floor((h - labelSize.height) / 2),
            width: labelSize.width,
            height: labelSize.height
        )

        // Cancel button: centered with same right offset
        cancelButton.sizeToFit()
        cancelButton.center = CGPoint(x: w / 2 + cancelRightShift, y: h / 2)
    }

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

    private func updateThemeColors() {
        redDot.backgroundColor = recordingDotColor
        timerLabel.textColor = panelControlColor
        arrowIcon.tintColor = panelControlColor
        slideLabel.textColor = panelControlColor
        cancelButton.setTitleColor(panelControlAccentColor, for: .normal)
    }

    // MARK: - Public API

    /// Updates timer text. Called from AudioRecorder.onLevelUpdate.
    func updateDuration(_ duration: TimeInterval) {
        let totalSeconds = Int(duration)
        let minutes = totalSeconds / 60
        let seconds = totalSeconds % 60
        let text = String(format: "%d:%02d", minutes, seconds)
        guard timerLabel.text != text else { return }
        timerLabel.text = text

        // Keep label width live-updated so long durations never render with ellipsis.
        let h = bounds.height
        guard h > 0 else {
            setNeedsLayout()
            return
        }
        let timerSize = timerLabel.sizeThatFits(CGSize(width: .greatestFiniteMagnitude, height: h))
        let timerY = floor((h - timerSize.height) / 2)
        let timerWidth = max(timerMinWidth, ceil(timerSize.width + 4))
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        timerLabel.frame = CGRect(x: timerX, y: timerY, width: timerWidth, height: timerSize.height)
        redDot.frame = CGRect(
            x: dotX,
            y: timerY + floor((timerSize.height - dotSize) / 2),
            width: dotSize,
            height: dotSize
        )
        CATransaction.commit()
    }

    /// Updates cancel indicator position based on horizontal drag.
    /// translation is negative (finger sliding left).
    func updateCancelTranslation(_ translation: CGFloat) {
        guard !isDisplayingCancel, isEntryAnimationComplete else { return }

        // Only apply transform when actually dragging (threshold 8pt)
        let drag = abs(translation)
        guard VoiceRecordingParityMath.shouldApplyCancelTransform(translation) else {
            cancelContainer.transform = .identity
            cancelContainer.alpha = 1
            return
        }

        let offset = drag - VoiceRecordingParityConstants.cancelTransformThreshold
        cancelContainer.transform = CGAffineTransform(translationX: -offset * 0.5, y: 0)
        let currentMinX = cancelContainer.frame.minX + cancelContainer.transform.tx
        cancelContainer.alpha = max(0, min(1, (currentMinX - 100) / 10))
    }

    /// Animate panel in. Called when recording begins.
    /// Telegram: spring 0.4–0.5s, dot/timer slide from left, cancel from right.
    func animateIn(panelWidth: CGFloat) {
        // Red dot: appear with scale 0.3→1.0, alpha 0→1, 0.15s (Telegram exact)
        redDot.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
        redDot.alpha = 0
        UIView.animate(withDuration: 0.15) {
            self.redDot.transform = .identity
            self.redDot.alpha = 1
        }

        // Start pulsing after dot appears (Telegram: keyframe, 0.5s cycle)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { [weak self] in
            self?.startDotPulsing()
        }

        // Timer: slide in from left, spring 0.5s
        timerLabel.alpha = 0
        timerLabel.transform = CGAffineTransform(translationX: -30, y: 0)
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.55, initialSpringVelocity: 0, options: []) {
            self.timerLabel.alpha = 1
            self.timerLabel.transform = .identity
        }

        // Cancel indicator: slide in from right, spring 0.4s (Telegram exact)
        cancelContainer.alpha = 1
        cancelContainer.transform = CGAffineTransform(translationX: panelWidth * 0.3, y: 0)
        UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.55, initialSpringVelocity: 0, options: []) {
            self.cancelContainer.transform = .identity
        }

        // Mark entry animation complete + start jiggle after spring settles
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
            self?.isEntryAnimationComplete = true
            self?.startCancelJiggle()
        }
    }

    /// Animate panel out. Called when recording ends normally (send).
    func animateOut(completion: (() -> Void)? = nil) {
        stopDotPulsing()
        stopCancelJiggle()

        // Telegram: 0.15s fade + scale for dot/timer
        UIView.animate(withDuration: 0.15, animations: {
            self.redDot.alpha = 0
            self.timerLabel.alpha = 0
            self.timerLabel.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
            self.cancelContainer.alpha = 0
            self.cancelButton.alpha = 0
        }, completion: { _ in
            self.removeFromSuperview()
            completion?()
        })
    }

    /// Cancel-specific dismiss: red dot "trash" animation + timer slide left.
    /// Telegram: MediaRecordingPanelComponent animateOut(dismissRecording:)
    func animateOutCancel(completion: (() -> Void)? = nil) {
        stopDotPulsing()
        stopCancelJiggle()
        isUserInteractionEnabled = false

        var didFinishBin = false
        var didFinishRest = false
        let completeIfReady: () -> Void = { [weak self] in
            guard let self else { return }
            if didFinishBin && didFinishRest {
                self.removeFromSuperview()
                completion?()
            }
        }

        // Bin Lottie now plays inside attachButton (ComposerView.playBinAnimationInAttachButton).
        // Panel just fades its elements out.
        didFinishBin = true
        redDot.alpha = 0

        // Timer: scale to 0, slide left.
        UIView.animate(withDuration: 0.2) {
            self.timerLabel.transform = CGAffineTransform(translationX: -30, y: 0)
                .scaledBy(x: 0.001, y: 0.001)
            self.timerLabel.alpha = 0
        }

        // Hide panel visuals quickly so only trash animation remains visible.
        UIView.animate(withDuration: 0.12) {
            self.glassBackground.alpha = 0
            self.redDot.alpha = 0
            self.cancelContainer.alpha = 0
            self.cancelButton.alpha = 0
        }

        // Remove after animation completes
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.16) {
            didFinishRest = true
            completeIfReady()
        }
    }

    /// Transition cancel indicator to "Cancel" button (locked state).
    /// Telegram: arrow+label shrink up (-22pt, scale 0.25), button grows down.
    func showCancelButton() {
        guard !isDisplayingCancel else { return }
        isDisplayingCancel = true
        stopCancelJiggle()

        // Cancel button starts small and offset
        cancelButton.transform = CGAffineTransform(translationX: 0, y: 22).scaledBy(x: 0.25, y: 0.25)

        UIView.animate(withDuration: 0.25) {
            // Arrow + label shrink up
            self.cancelContainer.alpha = 0
            self.cancelContainer.transform = CGAffineTransform(translationX: 0, y: -22).scaledBy(x: 0.25, y: 0.25)
        }

        UIView.animate(withDuration: 0.25) {
            self.cancelButton.alpha = 1
            self.cancelButton.transform = .identity
        }
    }

    // MARK: - Red Dot Pulsing (Telegram exact: keyframe, 0.5s, [1,1,0], autoReverse)

    private func startDotPulsing() {
        let anim = CAKeyframeAnimation(keyPath: "opacity")
        anim.values = [1.0, 1.0, 0.0]
        anim.keyTimes = [0.0, 0.4546, 0.9091]
        anim.duration = 0.5
        anim.autoreverses = true
        anim.repeatCount = .infinity
        redDot.layer.add(anim, forKey: "pulse")
    }

    private func stopDotPulsing() {
        redDot.layer.removeAnimation(forKey: "pulse")
    }

    // MARK: - Cancel Jiggle (Telegram exact: 6pt, 1.0s, easeInOut, autoReverse, infinite)

    private func startCancelJiggle() {
        guard bounds.width > 320 else { return } // Telegram: only on wider screens
        let anim = CABasicAnimation(keyPath: "transform")
        anim.toValue = CATransform3DMakeTranslation(6, 0, 0)
        anim.duration = 1.0
        anim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        anim.autoreverses = true
        anim.repeatCount = .infinity
        cancelContainer.layer.add(anim, forKey: "jiggle")
    }

    private func stopCancelJiggle() {
        cancelContainer.layer.removeAnimation(forKey: "jiggle")
    }

    // MARK: - Actions

    @objc private func cancelTapped() {
        delegate?.recordingPanelDidTapCancel(self)
    }
}
