import SwiftUI
import Lottie

// MARK: - Animation Cache

final class LottieAnimationCache {
    static let shared = LottieAnimationCache()
    private var cache: [String: LottieAnimation] = [:]
    private let lock = NSLock()

    private init() {}

    func animation(named name: String) -> LottieAnimation? {
        lock.lock()
        defer { lock.unlock() }
        if let cached = cache[name] {
            return cached
        }
        if let animation = LottieAnimation.named(name) {
            cache[name] = animation
            return animation
        }
        return nil
    }

    func preload(_ names: [String]) {
        for name in names {
            _ = animation(named: name)
        }
    }
}

// MARK: - LottieView

struct LottieView: UIViewRepresentable, Equatable {
    let animationName: String
    var loopMode: LottieLoopMode = .playOnce
    var animationSpeed: CGFloat = 1.5
    var isPlaying: Bool = true

    static func == (lhs: LottieView, rhs: LottieView) -> Bool {
        lhs.animationName == rhs.animationName &&
        lhs.loopMode == rhs.loopMode &&
        lhs.animationSpeed == rhs.animationSpeed &&
        lhs.isPlaying == rhs.isPlaying
    }

    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    final class Coordinator {
        var didPlayOnce = false
        var lastAnimationName = ""
    }

    func makeUIView(context: Context) -> LottieAnimationView {
        let animationView: LottieAnimationView
        if let cached = LottieAnimationCache.shared.animation(named: animationName) {
            animationView = LottieAnimationView(animation: cached)
        } else {
            animationView = LottieAnimationView(name: animationName)
        }
        animationView.contentMode = .scaleAspectFit
        animationView.loopMode = loopMode
        animationView.animationSpeed = animationSpeed
        animationView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        animationView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
        context.coordinator.lastAnimationName = animationName
        playIfNeeded(animationView, coordinator: context.coordinator)
        return animationView
    }

    func updateUIView(_ uiView: LottieAnimationView, context: Context) {
        if context.coordinator.lastAnimationName != animationName {
            if let cached = LottieAnimationCache.shared.animation(named: animationName) {
                uiView.animation = cached
            } else {
                uiView.animation = LottieAnimation.named(animationName)
            }
            context.coordinator.lastAnimationName = animationName
            context.coordinator.didPlayOnce = false
        }

        uiView.loopMode = loopMode
        uiView.animationSpeed = animationSpeed

        if !isPlaying {
            context.coordinator.didPlayOnce = false
            if uiView.isAnimationPlaying {
                uiView.stop()
            }
            return
        }

        playIfNeeded(uiView, coordinator: context.coordinator)
    }

    private func playIfNeeded(_ view: LottieAnimationView, coordinator: Coordinator) {
        if loopMode == .playOnce {
            guard !coordinator.didPlayOnce else { return }
            coordinator.didPlayOnce = true
            view.play()
            return
        }
        if !view.isAnimationPlaying {
            view.play()
        }
    }
}

// MARK: - Crossfading Lottie Container

struct CrossfadingLottieView: View {
    let animationName: String
    let animationID: Int

    var body: some View {
        LottieView(
            animationName: animationName,
            loopMode: .playOnce,
            animationSpeed: 1.5
        )
        .id(animationID)
        .transition(.opacity.animation(.easeInOut(duration: 0.4)))
    }
}
