//
//  ShareViewController.swift
//  ShareExtension
//
//  Created by Shadowfacts on 4/17/23.
//  Copyright © 2023 Shadowfacts. All rights reserved.
//

import SwiftUI
import UserAccounts
import ComposeUI
import UniformTypeIdentifiers
import TuskerPreferences
import Combine
import Pachyderm

class ShareViewController: UIViewController {
    
    private var state: State = .loading
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.tintColor = Preferences.shared.accentColor.color
        
        if let account = UserAccountsManager.shared.getMostRecentAccount() {
            Task { @MainActor in
                let draft = await createDraft(account: account)
                state = .ok
                
                let context = ShareMastodonContext(accountInfo: account)
                let host = ShareHostingController(draft: draft, mastodonContext: context)
                host.view.translatesAutoresizingMaskIntoConstraints = false
                addChild(host)
                self.view.addSubview(host.view)
                NSLayoutConstraint.activate([
                    host.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
                    host.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
                    host.view.topAnchor.constraint(equalTo: self.view.topAnchor),
                    host.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
                ])
                host.didMove(toParent: self)
            }
        } else {
            state = .notLoggedIn
        }
    }
    
    private func createDraft(account: UserAccountInfo) async -> Draft {
        async let (text, attachments) = getDraftConfigurationFromExtensionContext()
        
        // TODO: I really don't like that there's a network request in the hot path here, but we don't have easy access to AccountPreferences :/
        let serverPrefs = try? await Client(baseURL: account.instanceURL, accessToken: account.accessToken).run(Client.getPreferences()).0
        let visibility = Preferences.shared.defaultPostVisibility.resolved(withServerDefault: serverPrefs?.postingDefaultVisibility)
        
        let draft = DraftsPersistentContainer.shared.createDraft(
            accountID: account.id,
            text: await text,
            contentWarning: "",
            inReplyToID: nil,
            visibility: visibility,
            language: serverPrefs?.postingDefaultLanguage,
            localOnly: !(serverPrefs?.postingDefaultFederation ?? true)
        )
        
        for attachment in await attachments {
            DraftsPersistentContainer.shared.viewContext.insert(attachment)
        }
        draft.draftAttachments = await attachments
        
        return draft
    }
    
    private func getDraftConfigurationFromExtensionContext() async -> (String, [DraftAttachment]) {
        guard let extensionContext,
              let inputItem = (extensionContext.inputItems as? [NSExtensionItem])?.first else {
            return ("", [])
        }
        var text: String = ""
        var url: URL?
        var attachments: [DraftAttachment] = []
        
        for itemProvider in inputItem.attachments ?? [] {
            // attachments have the highest priority, but only given this heuristic
            // otherwise attachment decoding ends up being overzealous
            let likelyAttachment = [UTType.image, .movie].contains(where: { itemProvider.hasItemConformingToTypeIdentifier($0.identifier) })
            
            if likelyAttachment,
               let attachment: DraftAttachment = await getObject(from: itemProvider) {
                attachments.append(attachment)
            } else if let attached: NSURL = await getObject(from: itemProvider) {
                if url == nil {
                    url = attached as URL
                }
            } else if let s: NSString = await getObject(from: itemProvider) {
                if text.isEmpty {
                    text = s as String
                }
            }
        }
        
        if text.isEmpty,
           let s = inputItem.attributedTitle ?? inputItem.attributedContentText {
            text = s.string
        }
        
        if let url {
            if !text.isEmpty {
                text += "\n"
            }
            text += url.absoluteString
        }
        
        if !text.isEmpty {
            text = "\n\n\(text)"
        }
        return (text, attachments)
    }
    
    private func getObject<T: NSItemProviderReading>(from itemProvider: NSItemProvider) async -> T? {
        guard itemProvider.canLoadObject(ofClass: T.self) else {
            return nil
        }
        return await withCheckedContinuation({ continuation in
            itemProvider.loadObject(ofClass: T.self) { object, error in
                continuation.resume(returning: object as? T)
            }
        })
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if case .notLoggedIn = state {
            let alert = UIAlertController(title: "Not Logged In", message: "You need to log in to an account through the app before you can post.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [unowned self] _ in
                self.extensionContext!.cancelRequest(withError: Error.notLoggedIn)
            }))
            present(alert, animated: true)
        }
    }
    
    enum State {
        case loading
        case notLoggedIn
        case ok
    }
    
    enum Error: Swift.Error {
        case notLoggedIn
    }

}
