//
//  ComposeViewModel.swift
//  ShareActionExtension
//
//  Created by MainasuK Cirno on 2021-7-16.
//

import Foundation
import SwiftUI
import Combine
import CoreDataStack

class ComposeViewModel: ObservableObject {

    var disposeBag = Set<AnyCancellable>()

    @Published var authentication: MastodonAuthentication?

    @Published var backgroundColor: UIColor = .clear
    @Published var toolbarHeight: CGFloat = 0
    @Published var viewDidAppear = false

    @Published var avatarImageURL: URL?
    @Published var authorName: String = ""
    @Published var authorUsername: String = ""

    @Published var statusContent = ""
    @Published var statusPlaceholder = ""
    @Published var statusContentAttributedString = NSAttributedString()

    @Published var isContentWarningComposing = false
    @Published var contentWarningBackgroundColor = Color.secondary
    @Published var contentWarningPlaceholder = ""
    @Published var contentWarningContent = ""

    @Published private(set) var attachmentViewModels: [StatusAttachmentViewModel] = []

    @Published var characterCount = 0

    public init() {
        $statusContent
            .map { NSAttributedString(string: $0) }
            .assign(to: &$statusContentAttributedString)

        Publishers.CombineLatest3(
            $statusContent,
            $isContentWarningComposing,
            $contentWarningContent
        )
        .map { statusContent, isContentWarningComposing, contentWarningContent in
            var count = statusContent.count
            if isContentWarningComposing {
                count += contentWarningContent.count
            }
            return count
        }
        .assign(to: &$characterCount)

        // setup attribute updater
        $attachmentViewModels
            .receive(on: DispatchQueue.main)
            .debounce(for: 0.3, scheduler: DispatchQueue.main)
            .sink { attachmentViewModels in
                // drive upload state
                // make image upload in the queue
                for attachmentViewModel in attachmentViewModels {
                    // skip when prefix N task when task finish OR fail OR uploading
                    guard let currentState = attachmentViewModel.uploadStateMachine.currentState else { break }
                    if currentState is StatusAttachmentViewModel.UploadState.Fail {
                        continue
                    }
                    if currentState is StatusAttachmentViewModel.UploadState.Finish {
                        continue
                    }
                    if currentState is StatusAttachmentViewModel.UploadState.Uploading {
                        break
                    }
                    // trigger uploading one by one
                    if currentState is StatusAttachmentViewModel.UploadState.Initial {
                        attachmentViewModel.uploadStateMachine.enter(StatusAttachmentViewModel.UploadState.Uploading.self)
                        break
                    }
                }
            }
            .store(in: &disposeBag)

        #if DEBUG
        // avatarImageURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif")
        // authorName = "Alice"
        // authorUsername = "alice"
        #endif
    }

}

extension ComposeViewModel {
    func setupAttachmentViewModels(_ viewModels: [StatusAttachmentViewModel]) {
        attachmentViewModels = viewModels
        for viewModel in viewModels {
            // set delegate
            viewModel.delegate = self
            // set observed
            viewModel.objectWillChange.sink { [weak self] _ in
                guard let self = self else { return }
                self.objectWillChange.send()
            }
            .store(in: &viewModel.disposeBag)
            // bind authentication
            $authentication
                .assign(to: \.value, on: viewModel.authentication)
                .store(in: &viewModel.disposeBag)
        }
    }

    func removeAttachmentViewModel(_ viewModel: StatusAttachmentViewModel) {
        if let index = attachmentViewModels.firstIndex(where: { $0 === viewModel }) {
            attachmentViewModels.remove(at: index)
        }
    }
}

// MARK: - StatusAttachmentViewModelDelegate
extension ComposeViewModel: StatusAttachmentViewModelDelegate {
    func statusAttachmentViewModel(_ viewModel: StatusAttachmentViewModel, uploadStateDidChange state: StatusAttachmentViewModel.UploadState?) {
        // trigger event update
        DispatchQueue.main.async {
            self.attachmentViewModels = self.attachmentViewModels
        }
    }
}