//
//  ArticlePasteboardWriter.swift
//  NetNewsWire
//
//  Created by Brent Simmons on 11/6/17.
//  Copyright © 2017 Ranchero Software. All rights reserved.
//

import AppKit
import Articles
import AppKitExtras

extension Article: PasteboardWriterOwner {
	
	@MainActor public var pasteboardWriter: NSPasteboardWriting {
		return ArticlePasteboardWriter(article: self)
	}
}

@objc @MainActor final class ArticlePasteboardWriter: NSObject, NSPasteboardWriting {

	let article: Article
	static let articleUTI = "com.ranchero.article"
	static let articleUTIType = NSPasteboard.PasteboardType(rawValue: articleUTI)
	static let articleUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.article"
	static let articleUTIInternalType = NSPasteboard.PasteboardType(rawValue: articleUTIInternal)

	private lazy var renderedHTML: String = {
		let rendering = ArticleRenderer.articleHTML(article: article, theme: ArticleThemesManager.shared.currentTheme)
		return rendering.html
	}()

	init(article: Article) {
		self.article = article
	}

	// MARK: - NSPasteboardWriting

	nonisolated func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {

		MainActor.assumeIsolated {
			var types = [ArticlePasteboardWriter.articleUTIType]

			if let _ = article.preferredURL {
				types += [.URL]
			}
			types += [.string, .html, ArticlePasteboardWriter.articleUTIInternalType]

			return types
		}
	}

	nonisolated func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {

		MainActor.assumeIsolated {
			let plist: Any?
			
			switch type {
			case .html:
				return renderedHTML
			case .string:
				plist = plainText()
			case .URL:
				return article.preferredLink ?? ""
			case ArticlePasteboardWriter.articleUTIType:
				plist = exportDictionary()
			case ArticlePasteboardWriter.articleUTIInternalType:
				plist = internalDictionary()
			default:
				plist = nil
			}
			
			return plist
		}
	}
}

private extension ArticlePasteboardWriter {

	func plainText() -> String {
		var s = ""

		if let title = article.title {
			s += "\(title)\n\n"
		}
		if let text = article.contentText {
			s += "\(text)\n\n"
		}
		else if let summary = article.summary {
			s += "\(summary)\n\n"
		}
		else if let html = article.contentHTML {
			let convertedHTML = html.convertingToPlainText()
			s += "\(convertedHTML)\n\n"
		}

		if let link = article.link {
			s += "URL: \(link)\n\n"
		}
		if let externalLink = article.externalLink {
			s += "external URL: \(externalLink)\n\n"
		}

		s += "Date: \(article.logicalDatePublished)\n\n"

		if let feed = article.feed {
			s += "Feed: \(feed.nameForDisplay)\n"
			if let homePageURL = feed.homePageURL {
				s += "Home page: \(homePageURL)\n"
			}
			s += "URL: \(feed.url)"
		}

		return s
	}

	private struct Key {
		static let articleID = "articleID" // database ID, unique per account
		static let uniqueID = "uniqueID" // unique ID, unique per feed (guid, or possibly calculated)
		static let feedURL = "feedURL"
		static let feedID = "feedID" // may differ from feedURL if coming from a syncing system
		static let title = "title"
		static let contentHTML = "contentHTML"
		static let contentText = "contentText"
		static let url = "url" // usually a permalink
		static let externalURL = "externalURL" // usually not a permalink
		static let summary = "summary"
		static let imageURL = "imageURL"
		static let bannerImageURL = "bannerImageURL"
		static let datePublished = "datePublished"
		static let dateModified = "dateModified"
		static let dateArrived = "dateArrived"
		static let read = "read"
		static let starred = "starred"
		static let authors = "authors"

		// Author
		static let authorName = "name"
		static let authorURL = "url"
		static let authorAvatarURL = "avatarURL"
		static let authorEmailAddress = "emailAddress"

		// Internal
		static let accountID = "accountID"
	}

	func exportDictionary() -> [String: Any] {
		var d = [String: Any]()

		d[Key.articleID] = article.articleID
		d[Key.uniqueID] = article.uniqueID

		if let feed = article.feed {
			d[Key.feedURL] = feed.url
		}

		d[Key.feedID] = article.feedID
		d[Key.title] = article.title ?? nil
		d[Key.contentHTML] = article.contentHTML ?? nil
		d[Key.contentText] = article.contentText ?? nil
		d[Key.url] = article.rawLink ?? nil
		d[Key.externalURL] = article.rawExternalLink ?? nil
		d[Key.summary] = article.summary ?? nil
		d[Key.imageURL] = article.rawImageLink ?? nil
		d[Key.datePublished] = article.datePublished ?? nil
		d[Key.dateModified] = article.dateModified ?? nil
		d[Key.dateArrived] = article.status.dateArrived
		d[Key.authors] = authorDictionaries() ?? nil

		return d
	}

	func internalDictionary() -> [String: Any] {
		var d = exportDictionary()
		d[Key.accountID] = article.accountID
		return d
	}

	func authorDictionary(_ author: Author) -> [String: Any] {
		var d = [String: Any]()

		d[Key.authorName] = author.name ?? nil
		d[Key.authorURL] = author.url ?? nil
		d[Key.authorAvatarURL] = author.avatarURL ?? nil
		d[Key.authorEmailAddress] = author.emailAddress ?? nil

		return d
	}

	func authorDictionaries() -> [[String: Any]]? {
		guard let authors = article.authors, !authors.isEmpty else {
			return nil
		}
		return authors.map{ authorDictionary($0) }
	}
}

