//
//  AddFeedController.swift
//  Evergreen
//
//  Created by Brent Simmons on 8/28/16.
//  Copyright © 2016 Ranchero Software, LLC. All rights reserved.
//

import AppKit
import RSCore
import RSTree
import Data
import RSFeedFinder
import Account
import RSParser

// Run add-feed sheet.
// If it returns with URL and optional name,
//   run FeedFinder plus modal progress window.
//   If FeedFinder returns feed,
//      add feed.
//   Else,
//      display error sheet.

class AddFeedController: AddFeedWindowControllerDelegate, FeedFinderDelegate {

	private let hostWindow: NSWindow
	private var addFeedWindowController: AddFeedWindowController?
	private var userEnteredURL: URL?
	private var userEnteredFolder: Folder?
	private var userEnteredTitle: String?
	private var userEnteredAccount: Account?
	private var foundFeedURLString: String?
	private var titleFromFeed: String?
	private var feedFinder: FeedFinder?
	private var isFindingFeed = false
	private var bestFeedSpecifier: FeedSpecifier?
	
	init(hostWindow: NSWindow) {
		
		self.hostWindow = hostWindow
	}

	func showAddFeedSheet(_ urlString: String?, _ name: String?) {

		let folderTreeControllerDelegate = FolderTreeControllerDelegate()

		let rootNode = Node(representedObject: AccountManager.shared.localAccount, parent: nil)
		rootNode.canHaveChildNodes = true
		let folderTreeController = TreeController(delegate: folderTreeControllerDelegate, rootNode: rootNode)

		addFeedWindowController = AddFeedWindowController(urlString: urlString ?? urlStringFromPasteboard, name: name, folderTreeController: folderTreeController, delegate: self)
		addFeedWindowController!.runSheetOnWindow(hostWindow)
	}

	// MARK: AddFeedWindowControllerDelegate

	func addFeedWindowController(_: AddFeedWindowController, userEnteredURL url: URL, userEnteredTitle title: String?, container: Container) {

		closeAddFeedSheet(NSApplication.ModalResponse.OK)

		guard let accountAndFolderSpecifier = accountAndFolderFromContainer(container) else {
			return
		}
		let account = accountAndFolderSpecifier.account
		let folder = accountAndFolderSpecifier.folder

		if account.hasFeed(withURL: url.absoluteString) {
			showAlreadySubscribedError(url.absoluteString)
			return
		}

		userEnteredAccount = account
		userEnteredURL = url
		userEnteredFolder = folder
		userEnteredTitle = title

		findFeed()
	}

	func addFeedWindowControllerUserDidCancel(_: AddFeedWindowController) {

		closeAddFeedSheet(NSApplication.ModalResponse.cancel)
	}

	// MARK: FeedFinderDelegate

	public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set<FeedSpecifier>) {

		isFindingFeed = false
		endShowingProgress()
		
		if let error = feedFinder.initialDownloadError {
			if feedFinder.initialDownloadStatusCode == 404 {
				showNoFeedsErrorMessage()
			}
			else {
				showInitialDownloadError(error)
			}
			return
		}

		guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else {
			showNoFeedsErrorMessage()
			return
		}

		self.bestFeedSpecifier = bestFeedSpecifier
		self.foundFeedURLString = bestFeedSpecifier.urlString

		if let url = URL(string: bestFeedSpecifier.urlString) {

			InitialFeedDownloader.download(url) { (parsedFeed) in
				self.titleFromFeed = parsedFeed?.title
				self.addFeedIfPossible(parsedFeed)
			}
		}
		else {
			// Shouldn't happen.
			showNoFeedsErrorMessage()
		}
	}
}


private extension AddFeedController {

	var urlStringFromPasteboard: String? {
		if let urlString = NSPasteboard.rs_urlString(from: NSPasteboard.general) {
			return urlString.rs_normalizedURL()
		}
		return nil
	}
	
	struct AccountAndFolderSpecifier {
		let account: Account
		let folder: Folder?
	}

	func accountAndFolderFromContainer(_ container: Container) -> AccountAndFolderSpecifier? {

		if let account = container as? Account {
			return AccountAndFolderSpecifier(account: account, folder: nil)
		}
		if let folder = container as? Folder, let account = folder.account {
			return AccountAndFolderSpecifier(account: account, folder: folder)
		}
		return nil
	}

	func closeAddFeedSheet(_ returnCode: NSApplication.ModalResponse) {

		if let sheetWindow = addFeedWindowController?.window {
			hostWindow.endSheet(sheetWindow, returnCode: returnCode)
		}
	}


	func addFeedIfPossible(_ parsedFeed: ParsedFeed?) {

		// Add feed if not already subscribed-to.

		guard let account = userEnteredAccount else {
			assertionFailure("Expected account.")
			return
		}
		guard let feedURLString = foundFeedURLString else {
			assertionFailure("Expected feedURLString.")
			return
		}

		if account.hasFeed(withURL: feedURLString) {
			showAlreadySubscribedError(feedURLString)
			return
		}

		guard let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString) else {
			return
		}

		if let parsedFeed = parsedFeed {
			account.update(feed, with: parsedFeed, {})
		}

		if account.addFeed(feed, to: userEnteredFolder) {
			NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
		}
	}

	// MARK: Find Feeds

	func findFeed() {

		guard let url = userEnteredURL else {
			assertionFailure("Expected userEnteredURL.")
			return
		}
		
		isFindingFeed = true
		feedFinder = FeedFinder(url: url, delegate: self)
		
		beginShowingProgress()
	}

	// MARK: Errors

	func showAlreadySubscribedError(_ urlString: String) {

		let alert = NSAlert()
		alert.alertStyle = .informational
		alert.messageText = NSLocalizedString("Already subscribed", comment: "Feed finder")
		alert.informativeText = NSLocalizedString("Can’t add this feed because you’ve already subscribed to it.", comment: "Feed finder")

		alert.beginSheetModal(for: hostWindow)
	}

	func showInitialDownloadError(_ error: Error) {

		let alert = NSAlert()
		alert.alertStyle = .informational
		alert.messageText = NSLocalizedString("Download Error", comment: "Feed finder")

		let formatString = NSLocalizedString("Can’t add this feed because of a download error: “%@”", comment: "Feed finder")
		let errorText = NSString.localizedStringWithFormat(formatString as NSString, error.localizedDescription)
		alert.informativeText = errorText as String

		alert.beginSheetModal(for: hostWindow)
	}

	func showNoFeedsErrorMessage() {

		let alert = NSAlert()
		alert.alertStyle = .informational
		alert.messageText = NSLocalizedString("Feed not found", comment: "Feed finder")
		alert.informativeText = NSLocalizedString("Can’t add a feed because no feed was found.", comment: "Feed finder")

		alert.beginSheetModal(for: hostWindow)
	}

	// MARK: Progress

	func beginShowingProgress() {
		
		runIndeterminateProgressWithMessage(NSLocalizedString("Finding feed…", comment:"Feed finder"))
	}
	
	func endShowingProgress() {
		
		stopIndeterminateProgress()
		hostWindow.makeKeyAndOrderFront(self)
	}
}