import UIKit

// MARK: - GalleryHeroAnimator

/// Snapshot-based hero open/close animations (Telegram parity).
/// Uses CABasicAnimation on layer properties for GPU-accelerated 60fps transitions.
final class GalleryHeroAnimator {

    private weak var containerView: UIView?
    private weak var sourceView: UIView?
    private var snapshotView: UIImageView?
    private let sourceCornerRadius: CGFloat = 16

    // MARK: - Timing Constants (Telegram parity)

    private let heroPositionDuration: CFTimeInterval = 0.21
    private let heroBoundsDuration: CFTimeInterval = 0.25
    private let crossfadeInDuration: CFTimeInterval = 0.2
    private let crossfadeOutDuration: CFTimeInterval = 0.08
    private let backgroundFadeInDuration: CFTimeInterval = 0.2
    private let backgroundFadeOutDuration: CFTimeInterval = 0.25

    // MARK: - Init

    init(containerView: UIView, sourceView: UIView?) {
        self.containerView = containerView
        self.sourceView = sourceView
    }

    // MARK: - Open Animation (bubble → fullscreen)

    func animateOpen(
        image: UIImage,
        sourceFrame: CGRect,
        targetFrame: CGRect,
        backgroundView: UIView,
        contentView: UIView,
        completion: @escaping () -> Void
    ) {
        guard let containerView, sourceFrame != .zero else {
            // Fallback: simple fade
            backgroundView.alpha = 0
            contentView.alpha = 0
            UIView.animate(withDuration: 0.25) {
                backgroundView.alpha = 1
                contentView.alpha = 1
            } completion: { _ in completion() }
            return
        }

        // Hide source cell image
        sourceView?.alpha = 0

        // Create snapshot
        let snapshot = UIImageView(image: image)
        snapshot.contentMode = .scaleAspectFill
        snapshot.clipsToBounds = true
        snapshot.layer.cornerRadius = sourceCornerRadius
        snapshot.layer.cornerCurve = .continuous
        snapshot.frame = sourceFrame
        containerView.addSubview(snapshot)
        snapshotView = snapshot

        // Hide real content during animation
        contentView.alpha = 0
        backgroundView.alpha = 0

        // Animate background fade
        let bgFade = CABasicAnimation(keyPath: "opacity")
        bgFade.fromValue = 0
        bgFade.toValue = 1
        bgFade.duration = backgroundFadeInDuration
        bgFade.fillMode = .forwards
        bgFade.isRemovedOnCompletion = false
        backgroundView.layer.add(bgFade, forKey: "heroFadeIn")
        backgroundView.alpha = 1

        // Animate snapshot position
        let positionAnim = CABasicAnimation(keyPath: "position")
        positionAnim.fromValue = CGPoint(x: sourceFrame.midX, y: sourceFrame.midY)
        positionAnim.toValue = CGPoint(x: targetFrame.midX, y: targetFrame.midY)
        positionAnim.duration = heroPositionDuration
        positionAnim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        positionAnim.fillMode = .forwards
        positionAnim.isRemovedOnCompletion = false

        // Animate snapshot bounds
        let boundsAnim = CABasicAnimation(keyPath: "bounds.size")
        boundsAnim.fromValue = sourceFrame.size
        boundsAnim.toValue = targetFrame.size
        boundsAnim.duration = heroBoundsDuration
        boundsAnim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        boundsAnim.fillMode = .forwards
        boundsAnim.isRemovedOnCompletion = false

        // Animate corner radius
        let cornerAnim = CABasicAnimation(keyPath: "cornerRadius")
        cornerAnim.fromValue = sourceCornerRadius
        cornerAnim.toValue = 0
        cornerAnim.duration = heroBoundsDuration
        cornerAnim.fillMode = .forwards
        cornerAnim.isRemovedOnCompletion = false

        CATransaction.begin()
        CATransaction.setCompletionBlock { [weak self] in
            // Show real content, remove snapshot
            contentView.alpha = 1
            self?.snapshotView?.removeFromSuperview()
            self?.snapshotView = nil
            backgroundView.layer.removeAnimation(forKey: "heroFadeIn")
            completion()
        }

        snapshot.layer.add(positionAnim, forKey: "heroPosition")
        snapshot.layer.add(boundsAnim, forKey: "heroBounds")
        snapshot.layer.add(cornerAnim, forKey: "heroCorner")

        // Set final model values
        snapshot.layer.position = CGPoint(x: targetFrame.midX, y: targetFrame.midY)
        snapshot.layer.bounds = CGRect(origin: .zero, size: targetFrame.size)
        snapshot.layer.cornerRadius = 0

        CATransaction.commit()
    }

    // MARK: - Close Animation (fullscreen → bubble)

    func animateClose(
        image: UIImage,
        currentFrame: CGRect,
        backgroundView: UIView,
        contentView: UIView,
        completion: @escaping () -> Void
    ) {
        // Get fresh source frame (cell may have scrolled)
        let targetFrame: CGRect
        if let source = sourceView, source.window != nil {
            targetFrame = source.convert(source.bounds, to: nil)
        } else {
            // Source view gone — fallback fade
            animateFadeDismiss(backgroundView: backgroundView, contentView: contentView, completion: completion)
            return
        }

        guard let containerView, targetFrame != .zero else {
            animateFadeDismiss(backgroundView: backgroundView, contentView: contentView, completion: completion)
            return
        }

        // Create snapshot at current gallery position
        let snapshot = UIImageView(image: image)
        snapshot.contentMode = .scaleAspectFill
        snapshot.clipsToBounds = true
        snapshot.layer.cornerRadius = 0
        snapshot.layer.cornerCurve = .continuous
        snapshot.frame = currentFrame
        containerView.addSubview(snapshot)
        snapshotView = snapshot

        // Hide real content
        contentView.alpha = 0

        // Animate background fade out
        let bgFade = CABasicAnimation(keyPath: "opacity")
        bgFade.fromValue = backgroundView.alpha
        bgFade.toValue = 0
        bgFade.duration = backgroundFadeOutDuration
        bgFade.fillMode = .forwards
        bgFade.isRemovedOnCompletion = false
        backgroundView.layer.add(bgFade, forKey: "heroFadeOut")
        backgroundView.alpha = 0

        // Animate snapshot back to source
        let positionAnim = CABasicAnimation(keyPath: "position")
        positionAnim.fromValue = CGPoint(x: currentFrame.midX, y: currentFrame.midY)
        positionAnim.toValue = CGPoint(x: targetFrame.midX, y: targetFrame.midY)
        positionAnim.duration = heroBoundsDuration
        positionAnim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        positionAnim.fillMode = .forwards
        positionAnim.isRemovedOnCompletion = false

        let boundsAnim = CABasicAnimation(keyPath: "bounds.size")
        boundsAnim.fromValue = currentFrame.size
        boundsAnim.toValue = targetFrame.size
        boundsAnim.duration = heroBoundsDuration
        boundsAnim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        boundsAnim.fillMode = .forwards
        boundsAnim.isRemovedOnCompletion = false

        let cornerAnim = CABasicAnimation(keyPath: "cornerRadius")
        cornerAnim.fromValue = 0
        cornerAnim.toValue = sourceCornerRadius
        cornerAnim.duration = heroBoundsDuration
        cornerAnim.fillMode = .forwards
        cornerAnim.isRemovedOnCompletion = false

        CATransaction.begin()
        CATransaction.setCompletionBlock { [weak self] in
            self?.sourceView?.alpha = 1
            self?.snapshotView?.removeFromSuperview()
            self?.snapshotView = nil
            backgroundView.layer.removeAnimation(forKey: "heroFadeOut")
            completion()
        }

        snapshot.layer.add(positionAnim, forKey: "heroPosition")
        snapshot.layer.add(boundsAnim, forKey: "heroBounds")
        snapshot.layer.add(cornerAnim, forKey: "heroCorner")

        snapshot.layer.position = CGPoint(x: targetFrame.midX, y: targetFrame.midY)
        snapshot.layer.bounds = CGRect(origin: .zero, size: targetFrame.size)
        snapshot.layer.cornerRadius = sourceCornerRadius

        CATransaction.commit()
    }

    // MARK: - Fade Dismiss Fallback

    func animateFadeDismiss(
        backgroundView: UIView,
        contentView: UIView,
        completion: @escaping () -> Void
    ) {
        sourceView?.alpha = 1
        UIView.animate(withDuration: 0.25, animations: {
            backgroundView.alpha = 0
            contentView.alpha = 0
        }, completion: { _ in
            completion()
        })
    }

    /// Restores source view visibility without animation (called on dealloc safety).
    func restoreSourceView() {
        sourceView?.alpha = 1
        snapshotView?.removeFromSuperview()
    }
}
