package setting

import (
	"ALLinSSL/backend/public"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/joho/godotenv"
	"io"
	"net/http"
	"os"
	"strconv"
	"syscall"
	"time"
)

type Setting struct {
	Timeout    int    `json:"timeout" form:"timeout"`
	Secure     string `json:"secure" form:"secure"`
	Https      string `json:"https" form:"https"`
	Key        string `json:"key" form:"key"`
	Cert       string `json:"cert" form:"cert"`
	Username   string `json:"username" form:"username"`
	Password   string `json:"password" form:"password"`
	PluginPath string `json:"plugin_path" form:"plugin_path"`
}

func Get() (Setting, error) {
	var setting = Setting{
		Timeout: public.TimeOut,
		Secure:  public.Secure,
	}

	setting.Https = public.GetSettingIgnoreError("https")
	key, err := os.ReadFile("data/https/key.pem")
	if err != nil {
		key = []byte{}
	}
	cert, err := os.ReadFile("data/https/cert.pem")
	if err != nil {
		cert = []byte{}
	}
	setting.Key = string(key)
	setting.Cert = string(cert)
	s, err := public.NewSqlite("data/settings.db", "")
	if err != nil {
		return setting, err
	}
	defer s.Close()
	s.TableName = "users"
	data, err := s.Select()
	if err != nil {
		return setting, err
	}
	if len(data) == 0 {
		return setting, fmt.Errorf("no users found")
	}
	username := data[0]["username"].(string)
	setting.Username = username
	setting.PluginPath = public.GetSettingIgnoreError("plugin_dir")
	return setting, nil
}

func Save(setting *Setting) error {
	var restart bool
	var reload bool

	s, err := public.NewSqlite("data/settings.db", "")
	if err != nil {
		return err
	}
	defer s.Close()
	if setting.Username != "" || setting.Password != "" {
		s.TableName = "users"
		user, err := s.Where("id=1", []interface{}{}).Select()
		if err != nil {
			return err
		}
		if len(user) == 0 {
			return fmt.Errorf("no users found")
		}
		data := map[string]interface{}{}
		if setting.Username != "" {
			data["username"] = setting.Username
		}

		salt := user[0]["salt"].(string)
		passwd := setting.Password + salt
		// fmt.Println(passwd)
		keyMd5 := md5.Sum([]byte(passwd))
		passwdMd5 := hex.EncodeToString(keyMd5[:])
		if setting.Password != "" {
			data["password"] = passwdMd5
		}
		_, err = s.Where("id=1", []interface{}{}).Update(data)
		if err != nil {
			return err
		}
		reload = true
	}
	s.TableName = "settings"
	if setting.Timeout != 0 && setting.Timeout != public.TimeOut {
		s.Where("key = 'timeout'", []interface{}{}).Update(map[string]interface{}{"value": setting.Timeout})
		public.TimeOut = setting.Timeout
		restart = true
	}
	if setting.Secure != public.Secure {
		s.Where("key = 'secure'", []interface{}{}).Update(map[string]interface{}{"value": setting.Secure})
		public.TimeOut = setting.Timeout
		restart = true
	}
	if setting.PluginPath != "" && setting.PluginPath != public.GetSettingIgnoreError("plugin_dir") {
		public.UpdateSetting("plugin_dir", setting.PluginPath)
	}
	if setting.Https != "" {
		if setting.Https == "1" {
			if setting.Key == "" || setting.Cert == "" {
				return fmt.Errorf("key or cert is empty")
			}
			// fmt.Println(setting.Key, setting.Cert)
			err := public.ValidateSSLCertificate(setting.Cert, setting.Key)
			if err != nil {
				return err
			}
			// dir := filepath.Dir("data/https")
			if err := os.MkdirAll("data/https", os.ModePerm); err != nil {
				panic("创建目录失败: " + err.Error())
			}
			err = os.WriteFile("data/https/key.pem", []byte(setting.Key), 0644)
			// fmt.Println(err)
			os.WriteFile("data/https/cert.pem", []byte(setting.Cert), 0644)
		}
		s.Where("key = 'https'", []interface{}{}).Update(map[string]interface{}{"value": setting.Https})
		restart = true
	}
	if restart {
		Restart()
		return nil
	} else {
		if reload {
			public.LoginKey = public.GenerateUUID()
		}
	}
	return nil
}

func Shutdown() {
	go func() {
		time.Sleep(time.Millisecond * 100)
		public.ShutdownFunc()
	}()
	return
}

func Restart() {
	go func() {
		time.Sleep(time.Millisecond * 100)
		env, err := godotenv.Read("data/.env")
		if err != nil {
			env = map[string]string{
				"web":       "restart",
				"scheduler": "start",
			}
		}
		pidStr, err := os.ReadFile("data/pid")
		if err != nil {
			fmt.Println("Error reading pid file")
			return
		}
		err = godotenv.Write(env, "data/.env")
		if err != nil {
			fmt.Println("Error writing to .env file")
			return
		}
		pid, err := strconv.Atoi(string(pidStr))
		if err != nil {
			fmt.Println("Error converting pid to int:", err)
			return
		}
		process, err := os.FindProcess(pid)
		if err != nil {
			fmt.Println("Error finding process:", err)
			return
		}
		err = process.Signal(syscall.SIGHUP)
		if err != nil {
			fmt.Println("Error sending signal:", err)
			return
		}
	}()
	return
}

func GetVersion() (map[string]string, error) {
	version := "v1.1.1"
	update := "0"
	newVersionObj, err := http.Get("https://download.allinssl.com/version.json")
	if err != nil {
		newVersionObj, err = http.Get("https://node1.allinssl.com/version.json")
		if err != nil {
			return map[string]string{
				"version":     version,
				"new_version": version,
				"update":      update,
				"log":         "",
				"date":        "",
			}, nil
		}
	}
	defer newVersionObj.Body.Close()

	var newVersionData map[string]string
	body, _ := io.ReadAll(newVersionObj.Body)
	err = json.Unmarshal(body, &newVersionData)
	if err != nil {
		return nil, fmt.Errorf("failed to parse version data: %v", err)
	}
	if version != newVersionData["version"] {
		update = "1"
	}
	newVersionData["new_version"] = newVersionData["version"]
	newVersionData["version"] = version
	newVersionData["update"] = update
	return newVersionData, nil
}
