//
// © 2017-2020 Qualcomm Technologies, Inc. and/or its subsidiaries. All rights reserved.
//

import Foundation
import CocoaLumberjack

class LocalMusicLibraryExporterCache {
	// MARK: Private Properties
	private let cacheDirectory: URL
	private let maxCacheSize: UInt = 256 * 1024 * 1024
	
	// MARK: Initializers
	init () {
		let fm = FileManager.default
		let paths = fm.urls(for: .cachesDirectory, in: .userDomainMask)
		cacheDirectory = paths.first!.appendingPathComponent("media")
		try? FileManager.default.createDirectory(at: cacheDirectory,
												 withIntermediateDirectories: true,
												 attributes: nil)
	}
}

// MARK: - Public Methods
extension LocalMusicLibraryExporterCache {
	func dataForAssetURL(_ assetURL: URL) -> Data? {
		guard let fullPath = urlForAssetURL(assetURL) else {
			return nil
		}
		let data = try? Data(contentsOf: fullPath)
		return data
	}
	
	func urlForAssetURL(_ assetURL: URL) -> URL? {
		guard let query = URLComponents(url: assetURL, resolvingAgainstBaseURL: false)?.queryItems else {
			return nil
		}
		
		guard let idQuery = query.first(where: { $0.name == "id" }),
			let idValue = idQuery.value else {
				return nil
		}
		
		var ext = assetURL.pathExtension
		// The Apple APIs are squimish about pretending to work with MP3 so we change the name
		if ext == "mp3" {
			ext = "empeethree"
		}
		let filename = idValue + "." + ext
		
		let fullPath = cacheDirectory.appendingPathComponent(filename)
		return fullPath
	}
	
	func didAddToCache(_ assetURL: URL) {
		let currentSize = cacheSize()
		DDLogVerbose("Cache size currently: \(currentSize)")
		DDLogVerbose("Max Cache size: \(maxCacheSize)")
		
		if currentSize > maxCacheSize {
			trimCache(currentSize: currentSize)
		}
	}
}

// MARK: - Private Methods
extension LocalMusicLibraryExporterCache {
	private func cacheSize() -> UInt {
		let fm = FileManager.default
		var folderSize: UInt = 0
		do {
			let contents = try fm.contentsOfDirectory(at: cacheDirectory,
													  includingPropertiesForKeys: nil)
			try contents.forEach { file in
				let fileSize = try file.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0
				folderSize = folderSize + UInt(fileSize)
			}
		} catch let error {
			DDLogError("Error fetching Cache size: \(error)")
			folderSize = 0
		}
		return folderSize
	}
	
	private func trimCache(currentSize: UInt) {
		let fm = FileManager.default
		do {
			let contents = try fm.contentsOfDirectory(at: cacheDirectory,
													  includingPropertiesForKeys: nil)
			
			// Sorted newest first
			var sorted = contents.sorted(by: { (first, second) -> Bool in
				let firstAttrs = try? fm.attributesOfItem(atPath: first.path)
				let secondAttrs = try? fm.attributesOfItem(atPath: second.path)
				let firstDate = firstAttrs?[FileAttributeKey.modificationDate] as? Date ?? NSDate.distantPast
				let secondDate = secondAttrs?[FileAttributeKey.modificationDate] as? Date ?? NSDate.distantPast
				return firstDate > secondDate
			})
			
			var runningSize = currentSize
			while runningSize > maxCacheSize && sorted.count > 1 {
				let victim = sorted.last!
				let fileSize = try victim.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0
				DDLogVerbose("Removing: \(victim) size: \(fileSize)")
				try fm.removeItem(at: victim)
				runningSize = runningSize - UInt(fileSize)
				sorted.remove(at: sorted.count - 1)
			}
			
			DDLogVerbose("Cache size after trimming: \(runningSize)")
			
		} catch let error {
			DDLogError("Error triming Cache: \(error)")
			return
		}
	}
}
