import Foundation
import Observation

@Observable
final class SearchViewModel {

    var query: String = ""
    var results: [SearchResultDTO] = []
    var isSearching: Bool = false
    var error: BookStackError? = nil
    var selectedTypeFilter: SearchResultDTO.ContentType? = nil
    var selectedTagFilter: String? = nil
    var availableTags: [TagDTO] = []
    var isLoadingTags: Bool = false

    var recentSearches: [String] {
        get { UserDefaults.standard.stringArray(forKey: "recentSearches") ?? [] }
        set {
            var trimmed = newValue.prefix(10).map { $0 }
            UserDefaults.standard.set(trimmed, forKey: "recentSearches")
        }
    }

    private var searchTask: Task<Void, Never>?

    // MARK: - Search trigger (call from onChange of query)

    func onQueryChanged() {
        searchTask?.cancel()
        guard query.count >= 2 else {
            results = []
            return
        }
        searchTask = Task {
            try? await Task.sleep(for: .milliseconds(400))
            guard !Task.isCancelled else { return }
            await performSearch()
        }
    }

    // MARK: - Filter change

    func onFilterChanged() {
        guard query.count >= 2 else { return }
        searchTask?.cancel()
        searchTask = Task {
            await performSearch()
        }
    }

    // MARK: - Recent searches

    func addToRecent(_ query: String) {
        var recent = recentSearches.filter { $0 != query }
        recent.insert(query, at: 0)
        recentSearches = recent
    }

    func clearRecentSearches() {
        recentSearches = []
    }

    func loadAvailableTags() async {
        guard availableTags.isEmpty else { return }
        isLoadingTags = true
        do {
            availableTags = try await BookStackAPI.shared.fetchTags()
        } catch {
            AppLog(.warning, "Could not load tags for search: \(error.localizedDescription)", category: "Search")
        }
        isLoadingTags = false
    }

    // MARK: - Private

    private func performSearch() async {
        isSearching = true
        error = nil
        let typeLabel = selectedTypeFilter.map { " [type:\($0.rawValue)]" } ?? ""
        let tagLabel  = selectedTagFilter.map  { " [tag:\($0)]" } ?? ""
        AppLog(.info, "Search: \"\(query)\"\(typeLabel)\(tagLabel)", category: "Search")
        do {
            let response = try await BookStackAPI.shared.search(
                query: query,
                type: selectedTypeFilter,
                tag: selectedTagFilter
            )
            results = response.data
            AppLog(.info, "Search returned \(results.count) result(s) for \"\(query)\"", category: "Search")
            addToRecent(query)
        } catch let e as BookStackError {
            AppLog(.error, "Search failed for \"\(query)\": \(e.localizedDescription)", category: "Search")
            error = e
        } catch {
            AppLog(.error, "Search failed for \"\(query)\": \(error.localizedDescription)", category: "Search")
            self.error = .unknown(error.localizedDescription)
        }
        isSearching = false
    }
}
