import Defaults
import Siesta
import SwiftUI

final class SearchModel: ObservableObject {
    @Published var store = Store<[ContentItem]>()

    var accounts = AccountsModel()
    @Published var query = SearchQuery()
    @Published var queryText = ""
    @Published var querySuggestions = Store<[String]>()
    @Published var suggestionsText = ""

    @Published var fieldIsFocused = false

    private var previousResource: Resource?
    private var resource: Resource!

    var isLoading: Bool {
        resource?.isLoading ?? false
    }

    func changeQuery(_ changeHandler: @escaping (SearchQuery) -> Void = { _ in }) {
        changeHandler(query)

        let newResource = accounts.api.search(query)
        guard newResource != previousResource else {
            return
        }

        previousResource?.removeObservers(ownedBy: store)
        previousResource = newResource

        resource = newResource
        resource.addObserver(store)

        if !query.isEmpty {
            loadResourceIfNeededAndReplaceStore()
        }
    }

    func resetQuery(_ query: SearchQuery = SearchQuery()) {
        self.query = query

        let newResource = accounts.api.search(query)
        guard newResource != previousResource else {
            return
        }

        store.replace([])

        previousResource?.removeObservers(ownedBy: store)
        previousResource = newResource

        resource = newResource
        resource.addObserver(store)

        if !query.isEmpty {
            loadResourceIfNeededAndReplaceStore()
        }
    }

    func loadResourceIfNeededAndReplaceStore() {
        let currentResource = resource!

        if let request = resource.loadIfNeeded() {
            request.onSuccess { response in
                if let results: [ContentItem] = response.typedContent() {
                    self.replace(results, for: currentResource)
                }
            }
        } else {
            replace(store.collection, for: currentResource)
        }
    }

    func replace(_ videos: [ContentItem], for resource: Resource) {
        if self.resource == resource {
            store = Store<[ContentItem]>(videos)
        }
    }

    private var suggestionsDebounceTimer: Timer?

    func loadSuggestions(_ query: String) {
        guard !query.isEmpty else {
            return
        }

        suggestionsDebounceTimer?.invalidate()

        suggestionsDebounceTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
            let resource = self.accounts.api.searchSuggestions(query: query)

            resource.addObserver(self.querySuggestions)
            resource.loadIfNeeded()

            if let request = resource.loadIfNeeded() {
                request.onSuccess { response in
                    if let suggestions: [String] = response.typedContent() {
                        self.querySuggestions = Store<[String]>(suggestions)
                    }
                    self.suggestionsText = query
                }
            } else {
                self.querySuggestions = Store<[String]>(self.querySuggestions.collection)
                self.suggestionsText = query
            }
        }
    }
}