//
//  ActivityManager.swift
//  NetNewsWire-iOS
//
//  Created by Maurice Parker on 8/23/19.
//  Copyright © 2019 Ranchero Software. All rights reserved.
//

import Foundation
import CoreSpotlight
import CoreServices
import Account
import Articles
import Intents

class ActivityManager {
	
	private var nextUnreadActivity: NSUserActivity?
	private var selectingActivity: NSUserActivity?
	private var readingActivity: NSUserActivity?

	var stateRestorationActivity: NSUserActivity? {
		if readingActivity != nil {
			return readingActivity
		}
		return selectingActivity
	}
	
	init() {
		NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
	}
	
	func invalidateCurrentActivities() {
		invalidateReading()
		invalidateSelecting()
		invalidateNextUnread()
	}
	
	func selectingToday() {
		invalidateCurrentActivities()
		
		let title = NSLocalizedString("See articles for “Today”", comment: "Today")
		selectingActivity = makeSelectingActivity(type: ActivityType.selectToday, title: title, identifier: "smartfeed.today")
		selectingActivity!.becomeCurrent()
	}
	
	func selectingAllUnread() {
		invalidateCurrentActivities()
		
		let title = NSLocalizedString("See articles in “All Unread”", comment: "All Unread")
		selectingActivity = makeSelectingActivity(type: ActivityType.selectAllUnread, title: title, identifier: "smartfeed.allUnread")
		selectingActivity!.becomeCurrent()
	}
	
	func selectingStarred() {
		invalidateCurrentActivities()
		
		let title = NSLocalizedString("See articles in “Starred”", comment: "Starred")
		selectingActivity = makeSelectingActivity(type: ActivityType.selectStarred, title: title, identifier: "smartfeed.starred")
		selectingActivity!.becomeCurrent()
	}
	
	func selectingFolder(_ folder: Folder) {
		invalidateCurrentActivities()
		
		let localizedText = NSLocalizedString("See articles in  “%@”", comment: "See articles in Folder")
		let title = NSString.localizedStringWithFormat(localizedText as NSString, folder.nameForDisplay) as String
		selectingActivity = makeSelectingActivity(type: ActivityType.selectFolder, title: title, identifier: ActivityManager.identifer(for: folder))
	 
		selectingActivity!.userInfo = [
			ActivityID.accountID.rawValue: folder.account?.accountID ?? "",
			ActivityID.accountName.rawValue: folder.account?.name ?? "",
			ActivityID.folderName.rawValue: folder.nameForDisplay
		]

		selectingActivity!.becomeCurrent()
	}
	
	func selectingFeed(_ feed: Feed) {
		invalidateCurrentActivities()
		
		let localizedText = NSLocalizedString("See articles in  “%@”", comment: "See articles in Feed")
		let title = NSString.localizedStringWithFormat(localizedText as NSString, feed.nameForDisplay) as String
		selectingActivity = makeSelectingActivity(type: ActivityType.selectFeed, title: title, identifier: ActivityManager.identifer(for: feed))
		
		selectingActivity!.userInfo = [
			ActivityID.accountID.rawValue: feed.account?.accountID ?? "",
			ActivityID.accountName.rawValue: feed.account?.name ?? "",
			ActivityID.feedID.rawValue: feed.feedID
		]
		updateSelectingActivityFeedSearchAttributes(with: feed)
		
		selectingActivity!.becomeCurrent()
	}
	
	func invalidateSelecting() {
		selectingActivity?.invalidate()
		selectingActivity = nil
	}
	
	func selectingNextUnread() {
		guard nextUnreadActivity == nil else { return }
		let title = NSLocalizedString("See first unread article", comment: "First Unread")
		nextUnreadActivity = makeSelectingActivity(type: ActivityType.nextUnread, title: title, identifier: "action.nextUnread")
		nextUnreadActivity!.becomeCurrent()
	}
	
	func invalidateNextUnread() {
		nextUnreadActivity?.invalidate()
		nextUnreadActivity = nil
	}
	
	func reading(_ article: Article?) {
		invalidateReading()
		invalidateNextUnread()
		guard let article = article else { return }
		readingActivity = makeReadArticleActivity(article)
		readingActivity?.becomeCurrent()
	}
	
	func invalidateReading() {
		readingActivity?.invalidate()
		readingActivity = nil
	}
	
	static func cleanUp(_ account: Account) {
		var ids = [String]()
		
		if let folders = account.folders {
			for folder in folders {
				ids.append(identifer(for: folder))
			}
		}
		
		for feed in account.flattenedFeeds() {
			ids.append(contentsOf: identifers(for: feed))
		}
		
		NSUserActivity.deleteSavedUserActivities(withPersistentIdentifiers: ids) {}
	}
	
	static func cleanUp(_ folder: Folder) {
		var ids = [String]()
		ids.append(identifer(for: folder))
		
		for feed in folder.flattenedFeeds() {
			ids.append(contentsOf: identifers(for: feed))
		}
		
		NSUserActivity.deleteSavedUserActivities(withPersistentIdentifiers: ids) {}
	}
	
	static func cleanUp(_ feed: Feed) {
		NSUserActivity.deleteSavedUserActivities(withPersistentIdentifiers: identifers(for: feed)) {}
	}
	
	@objc func feedIconDidBecomeAvailable(_ note: Notification) {
		guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed, let activityFeedId = selectingActivity?.userInfo?[ActivityID.feedID.rawValue] as? String else {
			return
		}
		if activityFeedId == feed.feedID {
			updateSelectingActivityFeedSearchAttributes(with: feed)
		}
	}

}

// MARK: Private

private extension ActivityManager {
	
	func makeSelectingActivity(type: ActivityType, title: String, identifier: String) -> NSUserActivity {
		let activity = NSUserActivity(activityType: type.rawValue)
		activity.title = title
		activity.suggestedInvocationPhrase = title
		activity.keywords = Set(makeKeywords(title))
		activity.isEligibleForPrediction = true
		activity.isEligibleForSearch = true
		activity.persistentIdentifier = identifier
		return activity
	}
	
	func makeReadArticleActivity(_ article: Article) -> NSUserActivity {
		let activity = NSUserActivity(activityType: ActivityType.readArticle.rawValue)

		activity.title = article.title
		
		let feedNameKeywords = makeKeywords(article.feed?.nameForDisplay)
		let articleTitleKeywords = makeKeywords(article.title)
		let keywords = feedNameKeywords + articleTitleKeywords
		activity.keywords = Set(keywords)
		
		activity.userInfo = [
			ActivityID.accountID.rawValue: article.accountID,
			ActivityID.accountName.rawValue: article.account?.name ?? "",
			ActivityID.feedID.rawValue: article.feedID,
			ActivityID.articleID.rawValue: article.articleID
		]
		activity.isEligibleForSearch = true
		activity.isEligibleForPrediction = false
		activity.isEligibleForHandoff = true
		activity.persistentIdentifier = ActivityManager.identifer(for: article)
		
		// CoreSpotlight
		let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeCompositeContent as String)
		attributeSet.title = article.title
		attributeSet.contentDescription = article.summary
		attributeSet.keywords = keywords
		
		if let image = article.avatarImage() {
			attributeSet.thumbnailData = image.pngData()
		}
		
		activity.contentAttributeSet = attributeSet
		
		return activity
	}
	
	func makeKeywords(_ value: String?) -> [String] {
		return value?.components(separatedBy: " ").filter { $0.count > 2 } ?? []
	}
	
	func updateSelectingActivityFeedSearchAttributes(with feed: Feed) {
		
		let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
		attributeSet.title = feed.nameForDisplay
		attributeSet.keywords = makeKeywords(feed.nameForDisplay)
		if let image = appDelegate.feedIconDownloader.icon(for: feed) {
			attributeSet.thumbnailData = image.pngData()
		} else if let image = appDelegate.faviconDownloader.faviconAsAvatar(for: feed) {
			attributeSet.thumbnailData = image.pngData()
		}
		
		selectingActivity!.contentAttributeSet = attributeSet
		selectingActivity!.needsSave = true
		
	}
	
	static func identifer(for folder: Folder) -> String {
		return "account_\(folder.account!.accountID)_folder_\(folder.nameForDisplay)"
	}
	
	static func identifer(for feed: Feed) -> String {
		return "account_\(feed.account!.accountID)_feed_\(feed.feedID)"
	}
	
	static func identifer(for article: Article) -> String {
		return "account_\(article.accountID)_feed_\(article.feedID)_article_\(article.articleID)"
	}
	
	static func identifers(for feed: Feed) -> [String] {
		var ids = [String]()
		ids.append(identifer(for: feed))
		
		for article in feed.fetchArticles() {
			ids.append(identifer(for: article))
		}
		
		return ids
	}
	
}