import Foundation
import UIKit

/// Caches loaded images and supports loading from URL or bundle. Used by marker icon loading.
@objcMembers
public final class ImageCacheManager: NSObject {

    public static let shared = ImageCacheManager()

    /// Obj-C compatible singleton access (same as shared).
    public static var sharedInstance: ImageCacheManager { shared }

    private let cache = NSCache<NSString, UIImage>()
    private let queue = DispatchQueue(label: "ImageCacheManager.queue", qos: .userInitiated)

    override init() {
        super.init()
        cache.countLimit = 100
    }

    /// Loads an image from the given source (URL or bundle asset name). Completion is called on main queue.
    public func getImage(withSource source: String, completion: @escaping (UIImage?) -> Void) {
        guard !source.isEmpty else {
            DispatchQueue.main.async { completion(nil) }
            return
        }

        if let cached = cache.object(forKey: source as NSString) {
            DispatchQueue.main.async { completion(cached) }
            return
        }

        if source.hasPrefix("http://") || source.hasPrefix("https://") {
            loadFromURL(source, completion: completion)
        } else {
            loadFromBundle(source, completion: completion)
        }
    }

    public func clearCache() {
        cache.removeAllObjects()
    }

    // MARK: - Private

    private func loadFromURL(_ urlString: String, completion: @escaping (UIImage?) -> Void) {
        guard let url = URL(string: urlString) else {
            DispatchQueue.main.async { completion(nil) }
            return
        }

        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            var image: UIImage?
            if let data = data, error == nil, let img = UIImage(data: data) {
                image = img
                self?.cache.setObject(img, forKey: urlString as NSString)
            }
            DispatchQueue.main.async { completion(image) }
        }.resume()
    }

    private func loadFromBundle(_ source: String, completion: @escaping (UIImage?) -> Void) {
        queue.async { [weak self] in
            var image = UIImage(named: source)

            if image == nil, let path = Bundle.main.path(forResource: source, ofType: nil) {
                image = UIImage(contentsOfFile: path)
            }

            if image == nil {
                let baseName = (source as NSString).deletingPathExtension
                image = UIImage(named: baseName)
            }

            if let image = image {
                self?.cache.setObject(image, forKey: source as NSString)
            }

            DispatchQueue.main.async { completion(image) }
        }
    }
}
