package cmd

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

func IsExist(f string) bool {
	_, err := os.Stat(f)
	return err == nil || os.IsExist(err)
}

func apirequest_post(w http.ResponseWriter, req *http.Request) {
	var serverID = req.PathValue("id")
	if !IsExist("./" + serverID) {
		w.WriteHeader(403)
		fmt.Printf("Requesting ServerID: %s does not exist\n", serverID)
		return
	}
	file, err := os.Open("./" + serverID + "/server.json")
	if err != nil {
		w.WriteHeader(403)
		fmt.Printf("Requesting ServerID: %s file does not exist\n", serverID)
		return
	}
	defer file.Close()
	file_decoder := json.NewDecoder(file)
	var configs map[string]string
	file_decoder.Decode(&configs)

	decoder := json.NewDecoder(req.Body)
	var params map[string]string
	var resp string
	decoder.Decode(&params)
	for key, one := range params {
		//		fmt.Printf("%s key's value %s\n", key, one)
		if value, ok := configs[key]; ok {
			if value != one {
				if addr, ok := configs[key+"-update"]; ok {
					if resp != "" {
						resp += ","
					}
					resp += "{\"" + key + "\":\"" + value + "\",\"" + addr + "\":\"" + configs[addr] + "\"}"
				} else {
					w.WriteHeader(500)
					fmt.Printf("%s key update not found\n", key)
					return
				}
			} else {
				//				fmt.Printf("%s's request %s equals %s\n", key, one, value)
			}
		} else {
			w.WriteHeader(404)
			fmt.Printf("Requesting ServerID: %s, key:%s not found\n", serverID, key)
			return
		}
	}
	w.Write([]byte(resp))
}

func apirequest_get(w http.ResponseWriter, req *http.Request) {
	var serverID = req.PathValue("id")
	if !IsExist("./" + serverID) {
		w.WriteHeader(403)
		fmt.Printf("Requesting ServerID: %s does not exist\n", serverID)
		return
	}
	var fileid = req.PathValue("file")
	file, err := os.ReadFile("./" + serverID + "/data/" + fileid + ".json")
	fmt.Printf("requesting %s\n", "./"+serverID+"/data/"+fileid+".json")
	if err != nil {
		w.WriteHeader(403)
		fmt.Printf("Requesting ServerID: %s file %s does not exist\n", serverID, "./"+serverID+"/data/"+fileid+".json")
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write(file)
}

func apirequest_file(w http.ResponseWriter, req *http.Request) {
	var serverID = req.PathValue("id")
	if !IsExist("./" + serverID) {
		w.WriteHeader(403)
		fmt.Printf("Requesting ServerID: %s does not exist\n", serverID)
		return
	}
	var fileid = req.PathValue("file")
	file, err := os.ReadFile("./" + serverID + "/files/" + fileid)
	fmt.Printf("requesting %s\n", "./"+serverID+"/files/"+fileid)
	if err != nil {
		w.WriteHeader(403)
		fmt.Printf("Requesting ServerID: %s file %s does not exist\n", serverID, "./"+serverID+"/files/"+fileid)
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write(file)
}

func apirequest_info_post(w http.ResponseWriter, req *http.Request) {
	file, err := os.Open("./info.json")
	if err != nil {
		w.WriteHeader(403)
		fmt.Println("Requesting info.json file does not exist")
		return
	}
	defer file.Close()
	file_decoder := json.NewDecoder(file)
	var configs map[string]string
	file_decoder.Decode(&configs)

	decoder := json.NewDecoder(req.Body)
	var params map[string]string
	var resp string
	decoder.Decode(&params)
	for key, one := range params {
		//		fmt.Printf("%s key's value %s\n", key, one)
		if value, ok := configs[key]; ok {
			if value != one {
				if addr, ok := configs[key+"-update"]; ok {
					if new_info, ok := configs[key+"-info"]; ok {
						if resp != "" {
							resp += ","
						}
						resp += "{\"" + key + "\":\"" + value + "\",\"" + addr + "\":\"" + configs[addr] + "\",\"" + key + "-info\":\"" + new_info + "\"}"
					} else {
						w.WriteHeader(500)
						fmt.Printf("info's %s key update not found\n", key)
						return
					}
				} else {
					w.WriteHeader(500)
					fmt.Printf("info's %s key update not found\n", key)
					return
				}
			} else {
				//				fmt.Printf("%s's request %s equals %s\n", key, one, value)
			}
		} else {
			w.WriteHeader(404)
			fmt.Printf("Requesting info key:%s not found\n", key)
			return
		}
	}
	w.Write([]byte(resp))
}

var cfgFile string
var v bool
var servername string
var port string

func apirequest_info_get(w http.ResponseWriter, req *http.Request) {
	var fileid = req.PathValue("request")
	file, err := os.ReadFile("./api" + fileid + ".json")
	fmt.Printf("requesting %s\n", "./api"+fileid+".json")
	if err != nil {
		w.WriteHeader(403)
		fmt.Printf("Requesting file %s does not exist\n", "./api"+fileid+".json")
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write(file)
}
func runServer() {
	if v {
		versionPrint()
		os.Exit(0)
	}

	fmt.Println("config file:" + cfgFile)
	if cfgFile != "" {
		viper.SetConfigFile(cfgFile)
		viper.SetConfigType("yaml")
	} else {
		viper.SetConfigFile("./craapi.config")
		viper.SetConfigType("yaml")
	}

	if err := viper.ReadInConfig(); err != nil {
		fmt.Println("Can't read config:", err)
		os.Exit(1)
	}

	http.HandleFunc("POST /api/server/{id}", apirequest_post)
	http.HandleFunc("GET /api/server/{id}/{file}", apirequest_get)
	http.HandleFunc("/api/file/{id}/{file}", apirequest_file)
	http.HandleFunc("GET /api/info/{request}", apirequest_info_get)
	http.HandleFunc("POST /api/info", apirequest_info_post)
	fmt.Println("API Server is tring to run at:", viper.GetString("servername")+":"+viper.GetString("port"))
	err := http.ListenAndServe(viper.GetString("servername")+":"+viper.GetString("port"), nil)
	if err != nil {
		fmt.Println("HTTP SERVER failed,err:", err)
		return
	}
	fmt.Println("API server started successfully")
}

var rootCmd = &cobra.Command{
	Use:   "craapi",
	Short: "CRA-MC API is a minecraft servers API",
	Long: `CRA-MC API is a minecraft servers API which can manage mutiple minecraft servers' mods version or files
	`,
	Run: func(cmd *cobra.Command, args []string) {
		runServer()
	},
}

func init() {
	cobra.OnInitialize(initConfig)
	rootCmd.Flags().StringVarP(&cfgFile, "config", "c", "./craapi.config", "config file")
	rootCmd.Flags().StringVarP(&servername, "servername", "s", "", "servername (domain or ip address)")
	rootCmd.Flags().StringVarP(&port, "port", "p", "", "port number")
	viper.BindPFlag("servername", rootCmd.Flags().Lookup("servername"))
	viper.BindPFlag("port", rootCmd.Flags().Lookup("port"))
	rootCmd.Flags().BoolVarP(&v, "version", "v", false, "version")
	viper.SetDefault("servername", "")
	viper.SetDefault("port", "19999")
}

func initConfig() {
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
