import Defaults
import Siesta
import SwiftUI

final class SearchState: ObservableObject {
    @Published var store = Store<[Video]>()
    @Published var query = SearchQuery()

    @Published var querySuggestions = Store<[String]>()

    @Default(.searchQuery) private var queryText

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

    init() {
        let newQuery = query
        newQuery.query = queryText
        query = newQuery

        resource = InvidiousAPI.shared.search(newQuery)
    }

    var isLoading: Bool {
        resource.isLoading
    }

    func loadQuerySuggestions(_ query: String) {
        let resource = InvidiousAPI.shared.searchSuggestions(query: query)

        resource.addObserver(querySuggestions)
        resource.loadIfNeeded()

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

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

        let newResource = InvidiousAPI.shared.search(query)
        guard newResource != previousResource else {
            return
        }

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

        queryText = query.query

        resource = newResource
        resource.addObserver(store)
        loadResourceIfNeededAndReplaceStore()
    }

    func loadResourceIfNeededAndReplaceStore() {
        let currentResource = resource!

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

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