import UIKit
import SwiftUI
import UniformTypeIdentifiers

// Null implementation used when BookStax is not configured (no keychain credentials).
private struct NullShareAPIService: ShareAPIServiceProtocol {
    func fetchShelves() async throws -> [ShelfSummary] { [] }
    func fetchBooks(shelfId: Int) async throws -> [BookSummary] { [] }
    func fetchChapters(bookId: Int) async throws -> [ChapterSummary] { [] }
    func createPage(bookId: Int, chapterId: Int?, title: String, markdown: String) async throws -> PageResult {
        throw ShareAPIError.notConfigured
    }
}

/// Entry point for the BookStax Share Extension.
/// `NSExtensionPrincipalClass` in Info.plist points to this class.
final class ShareViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemGroupedBackground

        Task { @MainActor in
            let text      = await extractSharedText()
            let viewModel = makeViewModel(for: text)
            embedSwiftUI(viewModel: viewModel)
        }
    }

    // MARK: - ViewModel factory

    private func makeViewModel(for text: String) -> ShareViewModel {
        if let creds = ShareExtensionKeychainService.loadCredentials() {
            let api = ShareExtensionAPIService(
                serverURL:   creds.serverURL,
                tokenId:     creds.tokenId,
                tokenSecret: creds.tokenSecret
            )
            let defaults = UserDefaults(suiteName: "group.de.hanold.bookstax")
            return ShareViewModel(
                sharedText:   text,
                apiService:   api,
                serverURL:    creds.serverURL,
                isConfigured: true,
                defaults:     defaults
            )
        } else {
            return ShareViewModel(
                sharedText:   text,
                apiService:   NullShareAPIService(),
                serverURL:    "",
                isConfigured: false,
                defaults:     nil
            )
        }
    }

    // MARK: - SwiftUI embedding

    private func embedSwiftUI(viewModel: ShareViewModel) {
        let contentView = ShareExtensionView(
            viewModel:  viewModel,
            onCancel:   { [weak self] in self?.cancel() },
            onComplete: { [weak self] in self?.complete() },
            onOpenURL:  { [weak self] url in self?.open(url) }
        )
        let host = UIHostingController(rootView: contentView)
        addChild(host)
        view.addSubview(host.view)
        host.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            host.view.topAnchor.constraint(equalTo: view.topAnchor),
            host.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            host.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            host.view.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
        host.didMove(toParent: self)
    }

    // MARK: - Extension context actions

    private func cancel() {
        extensionContext?.cancelRequest(
            withError: NSError(
                domain: NSCocoaErrorDomain,
                code: NSUserCancelledError,
                userInfo: [NSLocalizedDescriptionKey: "Abgebrochen"]
            )
        )
    }

    private func complete() {
        extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
    }

    private func open(_ url: URL) {
        extensionContext?.open(url, completionHandler: nil)
    }

    // MARK: - Text extraction

    /// Extracts plain text or a URL string from the incoming NSExtensionItems.
    private func extractSharedText() async -> String {
        guard let items = extensionContext?.inputItems as? [NSExtensionItem] else { return "" }

        for item in items {
            for provider in item.attachments ?? [] {
                if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
                    if let text = try? await provider.loadItem(
                        forTypeIdentifier: UTType.plainText.identifier
                    ) as? String {
                        return text
                    }
                }
                if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
                    if let url = try? await provider.loadItem(
                        forTypeIdentifier: UTType.url.identifier
                    ) as? URL {
                        return url.absoluteString
                    }
                }
            }
        }
        return ""
    }
}
