//
//  AutocompleteHashtagsController.swift
//  ComposeUI
//
//  Created by Shadowfacts on 4/1/23.
//

import SwiftUI
import Combine
import Pachyderm

class AutocompleteHashtagsController: ViewController {
    unowned let composeController: ComposeController
    var mastodonController: ComposeMastodonContext { composeController.mastodonController }
    
    private var stateCancellable: AnyCancellable?
    private var searchTask: Task<Void, Never>?
    
    @Published var hashtags: [Hashtag] = []
    
    init(composeController: ComposeController) {
        self.composeController = composeController
        
        stateCancellable = composeController.$currentInput
            .compactMap { $0 }
            .flatMap { $0.autocompleteStatePublisher }
            .compactMap {
                if case .hashtag(let s) = $0 {
                    return s
                } else {
                    return nil
                }
            }
            .debounce(for: .milliseconds(250), scheduler: DispatchQueue.main)
            .sink { [unowned self] query in
                self.searchTask?.cancel()
                self.searchTask = Task { [weak self] in
                    await self?.queryChanged(query)
                }
            }
    }
    
    @MainActor
    private func queryChanged(_ query: String) async {
        guard !query.isEmpty else {
            hashtags = []
            return
        }
        
        let localHashtags = mastodonController.searchCachedHashtags(query: query)
        
        var onlyLocalTagsTask: Task<Void, any Error>?
        if !localHashtags.isEmpty {
            onlyLocalTagsTask = Task {
                // we only want to do the local-only search if the trends API call takes more than .25sec or it fails
                try await Task.sleep(nanoseconds: 250 * NSEC_PER_MSEC)
                self.updateHashtags(searchResults: [], trendingTags: [], localHashtags: localHashtags, query: query)
            }
        }
        
        async let trendingTags = try? mastodonController.run(Client.getTrendingHashtags()).0
        async let searchResults = try? mastodonController.run(Client.search(query: "#\(query)", types: [.hashtags])).0.hashtags
        
        let trends = await trendingTags ?? []
        let search = await searchResults ?? []
        
        onlyLocalTagsTask?.cancel()
        guard !Task.isCancelled else { return }
        
        updateHashtags(searchResults: search, trendingTags: trends, localHashtags: localHashtags, query: query)
    }
    
    @MainActor
    private func updateHashtags(searchResults: [Hashtag], trendingTags: [Hashtag], localHashtags: [Hashtag], query: String) {
        var addedHashtags = Set<String>()
        var hashtags = [(Hashtag, Int)]()
        for group in [searchResults, trendingTags, localHashtags] {
            for tag in group where !addedHashtags.contains(tag.name) {
                let (matched, score) = FuzzyMatcher.match(pattern: query, str: tag.name)
                if matched {
                    hashtags.append((tag, score))
                    addedHashtags.insert(tag.name)
                }
            }
        }
        self.hashtags = hashtags
            .sorted { $0.1 > $1.1 }
            .map(\.0)
    }
    
    private func autocomplete(with hashtag: Hashtag) {
        guard let currentInput = composeController.currentInput else { return }
        currentInput.autocomplete(with: "#\(hashtag.name)")
    }
    
    var view: some View {
        AutocompleteHashtagsView()
    }
    
    struct AutocompleteHashtagsView: View {
        @EnvironmentObject private var controller: AutocompleteHashtagsController
        
        var body: some View {
            ScrollView(.horizontal) {
                HStack(spacing: 8) {
                    ForEach(controller.hashtags, id: \.name) { hashtag in
                        Button(action: { controller.autocomplete(with: hashtag) }) {
                            Text(verbatim: "#\(hashtag.name)")
                                .foregroundColor(Color(uiColor: .label))
                        }
                        .frame(height: 30)
                        .padding(.vertical, 8)
                    }
                    
                    Spacer()
                }
                .padding(.horizontal, 8)
                .animation(.linear(duration: 0.2), value: controller.hashtags)
            }
        }
    }
    
    
}
