package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/huichen/wukong/types"
	"net/http"
	"os"
	"strconv"
	"time"
)

var (
	_httpServer *http.Server
)

type ParamIndex struct {
	DocId       string `json:"docId"`
	Content     string `json:"content"`
	ForceUpdate bool   `json:"forceUpdate"`
}

type ParamDelete struct {
	DocId       string `json:"docId"`
	ForceUpdate bool   `json:"forceUpdate"`
}

type ParamShutdown struct {
}

type ParamSearch struct {
	Keywords string `json:"keywords"`
}

type ReturnSearch types.SearchResponse

func startHttp(addr string) error {
	router := mux.NewRouter()
	v1 := router.PathPrefix("/v1").Subrouter()
	v1.HandleFunc("/index", index_v1).Methods("POST")
	v1.HandleFunc("/index", delete_index_v1).Methods("DELETE")
	v1.HandleFunc("/delete_index", delete_index_v1).Methods("POST")
	v1.HandleFunc("/search", search_v1).Methods("GET").Queries("keywords", "{keywords}",
		"offset", "{offset:[0-9]+}", "limit", "{offset:[0-9]+}")
	v1.HandleFunc("/shutdown", shutdown_v1).Methods("POST")
	v1.HandleFunc("/stat", stat_v1).Methods("GET")
	v1.HandleFunc("/index/{docId}", index_get_v1).Methods("GET")
	router.Use(mux.CORSMethodMiddleware(router))

	_httpServer = &http.Server{
		Handler:      router,
		Addr:         addr,
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  15 * time.Second,
	}

	go func() {
		if err := _httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			logger.Error().Err(err).Msg("http server error")
		}
	}()

	return nil
}

func stopHttp() {
	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()

	_httpServer.Shutdown(ctx)
}

func jsonBody(w *http.ResponseWriter, r *http.Request, v interface{}) error {
	//http.MaxBytesReader(*w, r.Body, 10)
	return json.NewDecoder(r.Body).Decode(v)
}

// new index
func index_v1(w http.ResponseWriter, r *http.Request) {
	var param ParamIndex
	err := jsonBody(&w, r, &param)
	if err != nil {
		logger.Error().Err(err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	logger.Trace().Interface("param", param).Msg("new index")

	searcher.IndexDocumentS(param.DocId, types.DocumentIndexData{
		Content: param.Content,
	}, param.ForceUpdate)

	w.WriteHeader(http.StatusOK)
	_, err = w.Write([]byte(`{"ec":0,"result":{}}`))
}

func index_get_v1(w http.ResponseWriter, r *http.Request) {

}

// delete index
func delete_index_v1(w http.ResponseWriter, r *http.Request) {
	var param ParamDelete
	err := jsonBody(&w, r, &param)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	logger.Trace().Interface("param", param).Msg("delete index")

	searcher.RemoveDocumentS(param.DocId, param.ForceUpdate)

	w.WriteHeader(http.StatusOK)
	_, err = w.Write([]byte(`{"ec":0,"result":{}}`))
}

// search
func search_v1(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	keywords := params["keywords"]
	offset, _ := strconv.ParseInt(params["offset"], 10, 64)
	limit, _ := strconv.ParseInt(params["limit"], 10, 64)

	logger.Trace().Interface("param", params).Msg("search")

	resp := searcher.Search(types.SearchRequest{
		Text: keywords,
		RankOptions: &types.RankOptions{
			OutputOffset: int(offset),
			MaxOutputs:   int(limit),
		},
	})
	sresp, err := json.Marshal(resp)
	if err != nil {
		logger.Error().Err(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, `{"ec":0,"result":%s}`, string(sresp))
}

func shutdown_v1(w http.ResponseWriter, r *http.Request) {
	logger.Trace().Msg("shutdown")
	chSig <- os.Interrupt
}

func stat_v1(w http.ResponseWriter, r *http.Request) {
	logger.Trace().Msg("stat")
	v := map[string]interface{}{
		"startAt":                  startedAt.Format("2006-01-02 15:04:05"),
		"version":                  version,
		"build":                    buildTime,
		"commit":                   gitCommit,
		"branch":                   gitBranch,
		"log":                      config.Logger.File,
		"logLevel":                 config.Logger.Level,
		"httpAddr":                 config.Server.Addr,
		"mode":                     config.Server.Mode,
		"indexType":                config.Engine.IndexType,
		"shardsNum":                config.Engine.ShardsNum,
		"persistent":               config.Engine.Persistent,
		"persistentEngine":         config.Engine.PersistentEngine,
		"persistentFolder":         config.Engine.PersistentFolder,
		"persistentShardsNum":      config.Engine.PersistentShardsNum,
		"numTokenIndexAdded":       searcher.NumTokenIndexAdded(),
		"numIndexingRequests":      searcher.NumIndexingRequests(),
		"numDocumentsIndexed":      searcher.NumDocumentsIndexed(),
		"numDocumentsRemoved":      searcher.NumDocumentsRemoved(),
		"numRemovingRequests":      searcher.NumRemovingRequests(),
		"numDocumentsForceUpdated": searcher.NumDocumentsForceUpdated(),
		"numForceUpdatingRequests": searcher.NumForceUpdatingRequests(),
		"numDocumentsStored":       searcher.NumDocumentsStored(),
	}
	b, err := json.Marshal(v)
	if err != nil {
		logger.Error().Err(err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, `{"ec":0,"result":%s}`, string(b))
}
