package setting

import (
	"ALLinSSL/backend/public"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/joho/godotenv"
	"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"`
}

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/data.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
	return setting, nil
}

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

	s, err := public.NewSqlite("data/data.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 != "" && setting.Secure != public.Secure {
		s.Where("key = 'secure'", []interface{}{}).Update(map[string]interface{}{"value": setting.Secure})
		public.TimeOut = setting.Timeout
		restart = true
	}
	if setting.Https == "1" && setting.Https != public.GetSettingIgnoreError("https") {
		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
		}
		s.Where("key = 'https'", []interface{}{}).Update(map[string]interface{}{"value": setting.Https})
		// 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)
		restart = true
	}
	if restart {
		Restart()
		return nil
	} else {
		if reload {
			s.Where("key = 'login_key'", []interface{}{}).Update(map[string]interface{}{"value": 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
}
