import UIKit

/// Telegram-parity circular progress ring for voice message downloads.
/// Overlays the 44×44 play button during CDN download.
///
/// Reference: `SemanticStatusNodeProgressContext.swift` in Telegram-iOS.
/// - Arc starts at 12 o'clock (-π/2), fills clockwise
/// - Rounded line caps, ~2pt stroke, 2.5pt inset
/// - Continuous rotation animation (4× speed)
/// - Cancel ✕ in center (12pt, 1.8pt stroke)
/// - Minimum visible progress: 2.7%
final class VoiceDownloadRingView: UIView {

    // MARK: - Telegram-exact constants

    private let lineWidth: CGFloat = 2.0
    private let inset: CGFloat = 2.5
    private let startAngle: CGFloat = -.pi / 2
    private let minProgress: CGFloat = 0.027
    private let cancelCrossSize: CGFloat = 12.0
    private let cancelLineWidth: CGFloat = 1.8

    // MARK: - Callbacks

    var onCancel: (() -> Void)?

    // MARK: - Layers

    private let progressLayer = CAShapeLayer()
    private let cancelLayer = CAShapeLayer()

    // MARK: - Init

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }

    private func setup() {
        isUserInteractionEnabled = true
        isHidden = true
        backgroundColor = .clear

        // Progress ring
        progressLayer.fillColor = nil
        progressLayer.lineCap = .round
        progressLayer.lineWidth = lineWidth
        progressLayer.strokeStart = 0
        progressLayer.strokeEnd = minProgress
        layer.addSublayer(progressLayer)

        // Cancel ✕
        cancelLayer.fillColor = nil
        cancelLayer.lineCap = .round
        cancelLayer.lineWidth = cancelLineWidth
        layer.addSublayer(cancelLayer)

        let tap = UITapGestureRecognizer(target: self, action: #selector(cancelTapped))
        addGestureRecognizer(tap)
    }

    // MARK: - Layout

    override func layoutSubviews() {
        super.layoutSubviews()
        let size = bounds.size
        guard size.width > 0 else { return }

        // Progress arc path (full circle — strokeEnd controls visible portion)
        let pathDiameter = size.width - lineWidth - inset * 2
        let radius = pathDiameter / 2
        let center = CGPoint(x: size.width / 2, y: size.height / 2)
        let circlePath = UIBezierPath(
            arcCenter: center,
            radius: radius,
            startAngle: startAngle,
            endAngle: startAngle + .pi * 2,
            clockwise: true
        )
        progressLayer.path = circlePath.cgPath
        progressLayer.frame = bounds

        // Cancel ✕ centered
        let half = cancelCrossSize / 2
        let crossPath = UIBezierPath()
        crossPath.move(to: CGPoint(x: center.x - half, y: center.y - half))
        crossPath.addLine(to: CGPoint(x: center.x + half, y: center.y + half))
        crossPath.move(to: CGPoint(x: center.x + half, y: center.y - half))
        crossPath.addLine(to: CGPoint(x: center.x - half, y: center.y + half))
        cancelLayer.path = crossPath.cgPath
        cancelLayer.frame = bounds
    }

    // MARK: - Public API

    /// Update ring color to match play button foreground.
    func setRingColor(_ color: UIColor) {
        progressLayer.strokeColor = color.cgColor
        cancelLayer.strokeColor = color.cgColor
    }

    /// Set download progress (0.0–1.0). Values below 2.7% are clamped up.
    func setProgress(_ value: CGFloat, animated: Bool = true) {
        let clamped = max(minProgress, min(1.0, value))

        if animated {
            let anim = CABasicAnimation(keyPath: "strokeEnd")
            anim.fromValue = progressLayer.presentation()?.strokeEnd ?? progressLayer.strokeEnd
            anim.toValue = clamped
            anim.duration = 0.2
            anim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
            anim.isRemovedOnCompletion = false
            anim.fillMode = .forwards
            progressLayer.add(anim, forKey: "progress")
        } else {
            progressLayer.removeAnimation(forKey: "progress")
        }
        progressLayer.strokeEnd = clamped
    }

    /// Show the ring and start rotation.
    func show() {
        guard isHidden else { return }
        isHidden = false
        alpha = 0
        UIView.animate(withDuration: 0.18) { self.alpha = 1 }
        startRotation()
    }

    /// Hide the ring and stop rotation.
    func hide() {
        guard !isHidden else { return }
        UIView.animate(withDuration: 0.18) {
            self.alpha = 0
        } completion: { _ in
            self.isHidden = true
            self.stopRotation()
            self.setProgress(self.minProgress, animated: false)
        }
    }

    // MARK: - Rotation

    private func startRotation() {
        guard progressLayer.animation(forKey: "rotation") == nil else { return }
        let rotation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotation.fromValue = 0
        rotation.toValue = CGFloat.pi * 2
        rotation.duration = 1.6  // ~4× per 2π normalized (Telegram: angle * 4.0)
        rotation.repeatCount = .infinity
        rotation.isRemovedOnCompletion = false
        progressLayer.add(rotation, forKey: "rotation")
    }

    private func stopRotation() {
        progressLayer.removeAnimation(forKey: "rotation")
    }

    // MARK: - Actions

    @objc private func cancelTapped() {
        onCancel?()
    }
}
