//
//  ArticleExtractor.swift
//  NetNewsWire
//
//  Created by Maurice Parker on 9/18/19.
//  Copyright © 2019 Ranchero Software. All rights reserved.
//

import Foundation
import Account
import Secrets

public enum ArticleExtractorState {
    case ready
    case processing
    case failedToParse
    case complete
	case cancelled
}

protocol ArticleExtractorDelegate {

	@MainActor func articleExtractionDidFail(with: Error)
	@MainActor func articleExtractionDidComplete(extractedArticle: ExtractedArticle)
}

final class ArticleExtractor {
	
	private var dataTask: URLSessionDataTask? = nil
    
    var state: ArticleExtractorState!
    var article: ExtractedArticle?
    var delegate: ArticleExtractorDelegate?
	var articleLink: String?
	
    private var url: URL!
    
	public init?(_ articleLink: String, secretsProvider: SecretsProvider) {
		self.articleLink = articleLink
		
		let clientURL = "https://extract.feedbin.com/parser"
		let username = secretsProvider.mercuryClientId
		let signature = articleLink.hmacUsingSHA1(key: secretsProvider.mercuryClientSecret)

		if let base64URL = articleLink.data(using: .utf8)?.base64EncodedString() {
			let fullURL = "\(clientURL)/\(username)/\(signature)?base64_url=\(base64URL)"
			if let url = URL(string: fullURL) {
				self.url = url
				return
			}
		}
		
		return nil
    }
    
    public func process() {
        
        state = .processing

        dataTask = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
            
            guard let self = self else { return }
            
            if let error = error {
                self.state = .failedToParse
                DispatchQueue.main.async {
                    self.delegate?.articleExtractionDidFail(with: error)
                }
                return
            }
            
            guard let data = data else {
                self.state = .failedToParse
                DispatchQueue.main.async {
					self.delegate?.articleExtractionDidFail(with: URLError(.cannotDecodeContentData))
                }
                return
            }
 
            do {
				let decoder = JSONDecoder()
				decoder.dateDecodingStrategy = .iso8601
				self.article = try decoder.decode(ExtractedArticle.self, from: data)
				
                DispatchQueue.main.async {
					if self.article?.content == nil {
						self.state = .failedToParse
						self.delegate?.articleExtractionDidFail(with: URLError(.cannotDecodeContentData))
					} else {
						self.state = .complete
						self.delegate?.articleExtractionDidComplete(extractedArticle: self.article!)
					}
                }
            } catch {
                self.state = .failedToParse
                DispatchQueue.main.async {
                    self.delegate?.articleExtractionDidFail(with: error)
                }
            }
            
        }
        
        dataTask!.resume()
		
    }
	
	public func cancel() {
		state = .cancelled
		dataTask?.cancel()
	}
    
}
