import Foundation

@objc(WMFPermanentCacheController)
public final class PermanentCacheController: NSObject {
    public let imageCache: ImageCacheController
    public let articleCache: ArticleCacheController
    let urlCache: PermanentlyPersistableURLCache
    let managedObjectContext: NSManagedObjectContext
    
    /// - Parameter moc: the managed object context for the cache
    /// - Parameter session: the session to utilize for image and article requests
    /// - Parameter configuration: the configuration to utilize for configuring requests
    /// - Parameter preferredLanguageDelegate: the preferredLanguageDelegate to utilize for determining the user's preferred languages
    @objc public init(moc: NSManagedObjectContext, session: Session, configuration: Configuration, preferredLanguageDelegate: WMFPreferredLanguageInfoProvider) {
        imageCache = ImageCacheController(moc: moc, session: session, configuration: configuration)
        articleCache = ArticleCacheController(moc: moc, imageCacheController: imageCache, session: session, configuration: configuration, preferredLanguageDelegate: preferredLanguageDelegate)
        urlCache = PermanentlyPersistableURLCache(moc: moc)
        managedObjectContext = moc
        super.init()
        session.permanentCache = self
    }
    
    /// Performs any necessary migrations on the CacheController's internal storage
    /// Exists on this @objc PermanentCacheController so it can be accessed from WMFDataStore
    @objc public static func setupCoreDataStack(_ completion: @escaping (NSManagedObjectContext?, Error?) -> Void) {
        CacheController.setupCoreDataStack(completion)
    }
    
    /// Performs any necessary teardown on CacheController's internal storage
    /// Exists on this @objc PermanentCacheController so it can be accessed from WMFDataStore
    @objc public func teardown(_ completion: @escaping () -> Void) {
        imageCache.cancelAllTasks()
        articleCache.cancelAllTasks()
        managedObjectContext.perform {
            // Give a bit of a buffer for other async activity to cease
            DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100), execute: completion)
        }
    }
    
    @objc public func fetchImage(withURL url: URL?, failure: @escaping (Error) -> Void, success: @escaping (ImageDownload) -> Void) {
        imageCache.fetchImage(withURL: url, failure: failure, success: success)
    }
    
    @objc public func memoryCachedImage(withURL url: URL) -> Image? {
        return imageCache.memoryCachedImage(withURL: url)
    }
    
    @objc public func fetchImage(withURL url: URL?, priority: Float, failure: @escaping (Error) -> Void, success: @escaping (ImageDownload) -> Void) -> String? {
        return imageCache.fetchImage(withURL: url, priority: priority, failure: failure, success: success)
    }
    
    @objc public func cancelImageFetch(withURL url: URL?, token: String?) {
        imageCache.cancelFetch(withURL: url, token: token)
    }
    
    @objc public func cachedImage(withURL url: URL?) -> Image? {
        return imageCache.cachedImage(withURL: url)
    }
    
    @objc public func fetchData(withURL url: URL, failure: @escaping (Error) -> Void, success: @escaping (Data, URLResponse) -> Void) {
        imageCache.fetchData(withURL: url, failure: failure, success: success)
    }
    
    @objc public func imageData(withURL url: URL) -> TypedImageData? {
        return imageCache.data(withURL: url)
    }
    #if TEST
    @objc public static func testController(with directory: URL, dataStore: MWKDataStore) -> PermanentCacheController {
        let moc = CacheController.createCacheContext(cacheURL: directory)!
        return PermanentCacheController(moc: moc, session: dataStore.session, configuration: dataStore.configuration, preferredLanguageDelegate: dataStore.languageLinkController)
    }
    #endif
}
