package wordsearch

import (
	"embed"
	"encoding/gob"
	"fmt"
	"log"
	"net/http"
	"sort"
	"text/template"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/ray1729/wordsearch/anagram"
	"github.com/ray1729/wordsearch/match"
	"github.com/rs/cors"
)

var anagramDB anagram.DB
var matchDB match.DB

func initializeDB() error {
	anagrams, err := fs.Open("data/anagram.bin")
	if err != nil {
		return err
	}
	defer anagrams.Close()
	if err := gob.NewDecoder(anagrams).Decode(&anagramDB); err != nil {
		return err
	}
	matches, err := fs.Open("data/match.bin")
	if err != nil {
		return err
	}
	defer matches.Close()
	if err := gob.NewDecoder(matches).Decode(&matchDB); err != nil {
		return err
	}
	return nil
}

func init() {
	log.Println("Initializing databases")
	if err := initializeDB(); err != nil {
		log.Fatal(err)
	}
	corsHandler := cors.New(cors.Options{
		AllowedOrigins:   []string{"*"},
		AllowedHeaders:   []string{"*"},
		AllowedMethods:   []string{http.MethodPost},
		AllowCredentials: false,
		MaxAge:           3600,
	})
	log.Println("Registering HTTP function with the Functions Framework")
	functions.HTTP("WordSearch", func(w http.ResponseWriter, r *http.Request) {
		corsHandler.ServeHTTP(w, r, handleFormSubmission)
	})
}

func handleFormSubmission(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		log.Printf("error parsing form: %v", err)
		http.Error(w, "error parsing form", http.StatusBadRequest)
		return
	}
	mode := r.Form.Get("mode")
	pattern := r.Form.Get("pattern")
	if len(pattern) == 0 {
		http.Error(w, "Missing pattern", http.StatusBadRequest)
		return
	}
	switch mode {
	case "match":
		results := matchResults(matchDB, pattern)
		renderTemplate(w, resultsTmpl, results)
	case "anagrams":
		results := anagramResults(anagramDB, pattern)
		renderTemplate(w, resultsTmpl, results)
	default:
		log.Printf("invalid mode: %s", mode)
		http.Error(w, fmt.Sprintf("Invalid mode: %s", mode), http.StatusBadRequest)
	}
}

func anagramResults(db anagram.DB, pattern string) ResultParams {
	var params ResultParams
	params.Results = db.FindAnagrams(pattern)
	if len(params.Results) > 0 {
		params.Preamble = fmt.Sprintf("Anagrams of %q:", pattern)
	} else {
		params.Preamble = fmt.Sprintf("Found no anagrams of %q", pattern)
	}
	sort.Slice(params.Results, func(i, j int) bool { return params.Results[i] < params.Results[j] })
	return params
}

func matchResults(db match.DB, pattern string) ResultParams {
	var params ResultParams
	params.Results = db.FindMatches(pattern)
	if len(params.Results) > 0 {
		params.Preamble = fmt.Sprintf("Matches for %q:", pattern)
	} else {
		params.Preamble = fmt.Sprintf("Found no matches for %q", pattern)
	}
	sort.Slice(params.Results, func(i, j int) bool { return params.Results[i] < params.Results[j] })
	return params
}

func renderTemplate(w http.ResponseWriter, t *template.Template, params any) {
	err := t.Execute(w, params)
	if err != nil {
		log.Printf("Error rendering template %s: %v", t.Name(), err)
		http.Error(w, "Internal server error", http.StatusInternalServerError)
	}
}

type ResultParams struct {
	Preamble string
	Results  []string
}

var resultsTmpl = template.Must(template.New("results").Parse(`
{{ with .Preamble }}
<p>{{ . }}</p>
{{ end }}
<ul>
{{ range .Results }}
  <li>
	  {{.}}
  	  <a href="https://dicoweb.gnu.org.ua/?q={{.}}&db=gcide&define=1" target="defn">GCIDE</a>
	  <a href="https://dicoweb.gnu.org.ua/?q={{.}}&db=WordNet&define=1" target="defn">WordNet</a>
	  <a href="https://dictionary.cambridge.org/dictionary/english/{{.}}" target="defn">Cambridge</a>
  </li>
{{ end }}
</ul>
`))

//go:embed data/*
var fs embed.FS
