//
//  SendPostIntentHandler.swift
//  MastodonIntent
//
//  Created by Cirno MainasuK on 2021-7-26.
//

import Foundation
import Intents
import Combine
import CoreData
import CoreDataStack
import MastodonSDK

final class SendPostIntentHandler: NSObject, SendPostIntentHandling {

    var disposeBag = Set<AnyCancellable>()

    lazy var coreDataStack = CoreDataStack()
    lazy var managedObjectContext = coreDataStack.persistentContainer.viewContext

    func handle(intent: SendPostIntent, completion: @escaping (SendPostIntentResponse) -> Void) {
        managedObjectContext.performAndWait {
            let request = MastodonAuthentication.sortedFetchRequest
            let authentications = (try? self.managedObjectContext.fetch(request)) ?? []
            let _authentication = authentications.sorted(by: { $0.activedAt > $1.activedAt }).first

            guard let authentication = _authentication else {
                let failureReason = APIService.APIError.implicit(.authenticationMissing).errorDescription ?? "Fail to Send Post"
                completion(SendPostIntentResponse.failure(failureReason: failureReason))
                return
            }

            let box = MastodonAuthenticationBox(
                authenticationRecord: .init(objectID: authentication.objectID),
                domain: authentication.domain,
                userID: authentication.userID,
                appAuthorization: .init(accessToken: authentication.appAccessToken),
                userAuthorization: .init(accessToken: authentication.userAccessToken)
            )

            let visibility: Mastodon.Entity.Status.Visibility = {
                switch intent.visibility {
                case .unknown:          return .public
                case .public:           return .public
                case .followersOnly:    return .private
                }
            }()
            let query = Mastodon.API.Statuses.PublishStatusQuery(
                status: intent.content,
                mediaIDs: nil,
                pollOptions: nil,
                pollExpiresIn: nil,
                inReplyToID: nil,
                sensitive: nil,
                spoilerText: nil,
                visibility: visibility
            )
            
            let idempotencyKey = UUID().uuidString

            Just(Void())
                .asyncMap {
                    try await APIService.shared.publishStatus(
                        domain: box.domain,
                        idempotencyKey: idempotencyKey,
                        query: query,
                        authenticationBox: box
                    )
                }
                .sink { _completion in
                    switch _completion {
                    case .failure(let error):
                        let failureReason = error.localizedDescription
                        completion(SendPostIntentResponse.failure(failureReason: failureReason))
                    case .finished:
                        break
                    }
                } receiveValue: { response in
                    let post = Post(identifier: response.value.id, display: intent.content ?? "")
                    post.url = URL(string: response.value.url ?? response.value.uri)
                    let result = SendPostIntentResponse(code: .success, userActivity: nil)
                    result.post = post
                    completion(result)
                }
                .store(in: &disposeBag)
        }

    }

    func resolveContent(for intent: SendPostIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
        guard let content = intent.content, !content.isEmpty else {
            completion(.needsValue())
            return
        }

        completion(.success(with: content))
    }

    func resolveVisibility(for intent: SendPostIntent, with completion: @escaping (PostVisibilityResolutionResult) -> Void) {
        completion(.success(with: intent.visibility))
    }



}
