//
//  ArticleThemeDownloader.swift
//  ArticleThemeDownloader
//
//  Created by Stuart Breckenridge on 20/09/2021.
//  Copyright © 2021 Ranchero Software. All rights reserved.
//

import Foundation
import Zip

struct ArticleThemeDownloader {

	public enum ArticleThemeDownloaderError: LocalizedError {
		case noThemeFile

		public var errorDescription: String? {
			switch self {
			case .noThemeFile:
				return "There is no NetNewsWire theme available."
			}
		}
	}

	static func handleFile(at location: URL) throws {
		createDownloadDirectoryIfRequired()
		let movedFileLocation = try moveTheme(from: location)
		let unzippedFileLocation = try unzipFile(at: movedFileLocation)
		NotificationCenter.default.post(name: .didEndDownloadingTheme, object: nil, userInfo: ["url" : unzippedFileLocation])
	}

	/// Removes downloaded themes, where themes == folders, from `Application Support/NetNewsWire/Downloads`.
	static func cleanUp() {
		guard let filenames = try? FileManager.default.contentsOfDirectory(atPath: downloadDirectory().path) else {
			return
		}
		for path in filenames {
			do {
				if FileManager.default.isFolder(atPath: downloadDirectory().appendingPathComponent(path).path) {
					try FileManager.default.removeItem(atPath: downloadDirectory().appendingPathComponent(path).path)
				}
			} catch {
				print(error)
			}
		}
	}
}

private extension ArticleThemeDownloader {

	/// Creates `Application Support/NetNewsWire/Downloads` if needed.
	static func createDownloadDirectoryIfRequired() {
		try? FileManager.default.createDirectory(at: downloadDirectory(), withIntermediateDirectories: true, attributes: nil)
	}
	
	/// Moves the downloaded `.tmp` file to the `downloadDirectory` and renames it a `.zip`
	/// - Parameter location: The temporary file location.
	/// - Returns: Destination `URL`.
	static func moveTheme(from location: URL) throws -> URL {
		var tmpFileName = location.lastPathComponent
		tmpFileName = tmpFileName.replacingOccurrences(of: ".tmp", with: ".zip")
		let fileUrl = downloadDirectory().appendingPathComponent("\(tmpFileName)")
		try FileManager.default.moveItem(at: location, to: fileUrl)
		return fileUrl
	}
	
	/// Unzips the zip file
	/// - Parameter location: Location of the zip archive.
	/// - Returns: Enclosed `.nnwtheme` file.
	static func unzipFile(at location: URL) throws -> URL {
		do {
			let unzipDirectory = URL(fileURLWithPath: location.path.replacingOccurrences(of: ".zip", with: ""))
			try Zip.unzipFile(location, destination: unzipDirectory, overwrite: true, password: nil, progress: nil, fileOutputHandler: nil) // Unzips to folder in Application Support/NetNewsWire/Downloads
			try FileManager.default.removeItem(at: location) // Delete zip in Cache
			let themeFilePath = findThemeFile(in: unzipDirectory.path)
			if themeFilePath == nil {
				throw ArticleThemeDownloaderError.noThemeFile
			}
			return URL(fileURLWithPath: unzipDirectory.appendingPathComponent(themeFilePath!).path)
		} catch {
			try? FileManager.default.removeItem(at: location)
			throw error
		}
	}
	
	/// Performs a deep search of the unzipped directory to find the theme file.
	/// - Parameter searchPath: directory to search
	/// - Returns: optional `String`
	static func findThemeFile(in searchPath: String) -> String? {
		if let directoryContents = FileManager.default.enumerator(atPath: searchPath) {
			while let file = directoryContents.nextObject() as? String {
				if file.hasSuffix(".nnwtheme") {
					return file
				}
			}
		}
		
		return nil
	}
	
	/// The download directory used by the theme downloader: `Application Support/NetNewsWire/Downloads`
	/// - Returns: `URL`
	static func downloadDirectory() -> URL {
		FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("NetNewsWire/Downloads", isDirectory: true)
	}
}
