<commit_before>package server

import (
	"fmt"
	"net/http"
	"strings"
	"sync"

	"github.com/zquestz/s/providers"
	"golang.org/x/text/language"
)

var (
	clientLocaleMutex sync.Mutex
)

func search(defaultProvider string, verbose bool, w http.ResponseWriter, r *http.Request) {
	var locale string

	requestedProvider := r.FormValue("provider")
	requestedTag := r.FormValue("tag")
	acceptLanguageHeader := r.Header.Get("Accept-Language")

	if strings.TrimSpace(acceptLanguageHeader) != "" {
		locale = parseAcceptLanguage(acceptLanguageHeader)
	}

	if requestedProvider == "" && requestedTag == "" {
		requestedProvider = defaultProvider
	}

	provider := providers.Providers[requestedProvider]
	if provider == nil && requestedTag == "" {
		providerNotFound(requestedProvider, w, r)
		return
	}

	builders := []providers.Provider{}

	if provider != nil {
		builders = append(builders, provider)
	}

	if requestedTag != "" {
		builders = append(builders, providers.GetProvidersByTag(requestedTag)...)
	}

	if len(builders) == 0 {
		tagProvidersNotFound(requestedTag, w, r)
		return
	}

	if query := r.FormValue("q"); query != "" {
		uris := executeSearch(builders, query, locale)

		if verbose {
			for _, uri := range uris {
				fmt.Printf("%s\n", uri)
			}
		}

		if len(uris) == 1 {
			http.Redirect(w, r, uris[0], 301)
			return
		}

		searchTemplate(uris, w)
		return
	}

	queryNotFound(w, r)
}

func executeSearch(providerList []providers.Provider, query, locale string) []string {
	uris := make([]string, 0, len(providerList))

	clientLocaleMutex.Lock()
	defer clientLocaleMutex.Unlock()

	providers.SetClientLocale(locale)

	for _, p := range providerList {
		uris = append(uris, p.BuildURI(query))
	}

	return uris
}

func providerNotFound(provider string, w http.ResponseWriter, r *http.Request) {
	http.Error(w, fmt.Sprintf("Provider %q not found.", provider), 400)
}

func tagProvidersNotFound(tag string, w http.ResponseWriter, r *http.Request) {
	http.Error(w, fmt.Sprintf("No providers matched tag %q.", tag), 400)
}

func queryNotFound(w http.ResponseWriter, r *http.Request) {
	http.Error(w, fmt.Sprintf("A search query is required."), 400)
}

func parseAcceptLanguage(header string) string {
	tags, _, err := language.ParseAcceptLanguage(header)
	if err != nil {
		return ""
	}

	// Using the first Tag as Tags are sorted by highest weight first<commit_msg>Added tag and provider expansion for the web interface<commit_after>package server

import (
	"fmt"
	"net/http"
	"strings"
	"sync"

	"github.com/zquestz/s/providers"
	"golang.org/x/text/language"
)

var (
	clientLocaleMutex sync.Mutex
)

func search(defaultProvider string, verbose bool, w http.ResponseWriter, r *http.Request) {
	var locale string
	var err error

	requestedProvider := r.FormValue("provider")
	requestedTag := r.FormValue("tag")
	acceptLanguageHeader := r.Header.Get("Accept-Language")

	if strings.TrimSpace(acceptLanguageHeader) != "" {
		locale = parseAcceptLanguage(acceptLanguageHeader)
	}

	if requestedProvider == "" && requestedTag == "" {
		requestedProvider = defaultProvider
	}

	if requestedProvider != "" {
		requestedProvider, err = providers.ExpandProvider(requestedProvider)
		if err != nil {
			expandNotValid(err, w, r)
			return
		}
	}

	provider := providers.Providers[requestedProvider]
	builders := []providers.Provider{}

	if provider != nil {
		builders = append(builders, provider)
	}

	if requestedTag != "" {
		requestedTag, err = providers.ExpandTag(requestedTag)
		if err != nil {
			expandNotValid(err, w, r)
			return
		}

		builders = append(builders, providers.GetProvidersByTag(requestedTag)...)
	}

	if query := r.FormValue("q"); query != "" {
		uris := executeSearch(builders, query, locale)

		if verbose {
			for _, uri := range uris {
				fmt.Printf("%s\n", uri)
			}
		}

		if len(uris) == 1 {
			http.Redirect(w, r, uris[0], http.StatusMovedPermanently)
			return
		}

		searchTemplate(uris, w)
		return
	}

	queryNotFound(w, r)
}

func executeSearch(providerList []providers.Provider, query, locale string) []string {
	uris := make([]string, 0, len(providerList))

	clientLocaleMutex.Lock()
	defer clientLocaleMutex.Unlock()

	providers.SetClientLocale(locale)

	for _, p := range providerList {
		uris = append(uris, p.BuildURI(query))
	}

	return uris
}

func queryNotFound(w http.ResponseWriter, r *http.Request) {
	http.Error(w, fmt.Sprintf("A search query is required."), http.StatusBadRequest)
}

func expandNotValid(err error, w http.ResponseWriter, r *http.Request) {
	http.Error(w, err.Error(), http.StatusBadRequest)
}

func parseAcceptLanguage(header string) string {
	tags, _, err := language.ParseAcceptLanguage(header)
	if err != nil {
		return ""
	}

	// Using the first Tag as Tags are sorted by highest weight first
{'fraction_non_alphanumeric': 0.13310938484148646, 'fraction_numerical': 0.0039890825110224645, 'mean_word_length': 4.407491486946651, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6296265', 'n_tokens_mistral': 1682, 'n_tokens_neox': 1558, 'n_words': 370}