package component

import (
	"encoding/json"
	"errors"
	"fmt"
	"gitee.ltd/lxh/logger/log"
	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
	"gopkg.in/fsnotify/fsnotify.v1"
	"os"
	"strings"
	"time"
	"wireguard-ui/command"
	"wireguard-ui/config"
	"wireguard-ui/global/client"
	"wireguard-ui/model"
	"wireguard-ui/service"
	"wireguard-ui/template/render_data"
)

type WireguardComponent struct{}

func Wireguard() WireguardComponent {
	return WireguardComponent{}
}

// GetClients
// @description: 获取所有链接的客户端信息
// @receiver w
// @return peers
// @return err
func (w WireguardComponent) GetClients() (peers []wgtypes.Peer, err error) {
	device, err := client.WireguardClient.Devices()
	if err != nil {
		return
	}

	for _, v := range device {
		return v.Peers, nil
	}

	return
}

// GetClientByPublicKey
// @description: 根据公钥获取指定客户端信息
// @receiver w
// @return peer
// @return err
func (w WireguardComponent) GetClientByPublicKey(pk string) (peer *wgtypes.Peer, err error) {
	peers, err := w.GetClients()
	if err != nil {
		return
	}

	for _, v := range peers {
		if v.PublicKey.String() == pk {
			return &v, nil
		}
	}

	return
}

// GenerateClientFile
// @description: 生成客户端文件
// @receiver w
// @param clientInfo
// @param server
// @param setting
// @return filePath
// @return err
func (w WireguardComponent) GenerateClientFile(clientInfo *model.Client, server *render_data.Server, setting *render_data.ServerSetting) (filePath string, err error) {
	var keys render_data.Keys
	_ = json.Unmarshal([]byte(clientInfo.Keys), &keys)

	var serverDNS []string
	if clientInfo.UseServerDns == 1 {
		serverDNS = setting.DnsServer
	}

	// 处理一下数据
	execData := render_data.ClientConfig{
		PrivateKey:          keys.PrivateKey,
		IpAllocation:        clientInfo.IpAllocation,
		MTU:                 setting.MTU,
		DNS:                 strings.Join(serverDNS, ","),
		PublicKey:           server.PublicKey,
		PresharedKey:        keys.PresharedKey,
		AllowedIPS:          clientInfo.AllowedIps,
		Endpoint:            setting.EndpointAddress,
		ListenPort:          int(server.ListenPort),
		PersistentKeepalive: setting.PersistentKeepalive,
	}

	// 不同环境下处理文件路径
	var outPath = "/tmp/" + fmt.Sprintf("%s.conf", clientInfo.Name)
	var templatePath = "./template/wg.client.conf"
	if os.Getenv("GIN_MODE") != "release" {
		outPath = "E:\\Workspace\\Go\\wireguard-ui\\template\\tmp\\" + fmt.Sprintf("%s.conf", clientInfo.Name)
		templatePath = "E:\\Workspace\\Go\\wireguard-ui\\template\\conf\\wg.client.conf"
	}

	err = Template().Execute(templatePath, outPath, execData)
	if err != nil {
		return "", errors.New("文件渲染失败")
	}

	return outPath, nil
}

// ServerControl
// @description: 服务端控制
// @receiver w
// @return error
func (w WireguardComponent) ServerControl(filePath string) {
	if filePath == "" {
		data, err := service.Setting().GetWGSetForConfig()
		if err != nil {
			log.Errorf("获取服务端配置失败: %v", err.Error())
			return
		}
		filePath = data.ConfigFilePath
	}
	w.watchConfigFile(filePath, config.Config.Wireguard.RestartMode, config.Config.Wireguard.DelayTime)
}

// watchConfigFile
// @description: 监听并重新操作配置文件
// @receiver w
// @param filepath
func (w WireguardComponent) watchConfigFile(filepath string, mode string, delay int64) {
	go func() {
		watcher, err := fsnotify.NewWatcher()
		if err != nil {
			log.Errorf("创建文件监控失败: %v", err.Error())
			return
		}

		defer watcher.Close()
		done := make(chan bool)

		go func() {
			for {
				select {
				case event, ok := <-watcher.Events:
					if !ok {
						return
					}

					if event.Op == fsnotify.Write {
						switch mode {
						case "NOW":
							command.RestartWireguard(false, filepath)
						case "DELAY":
							time.Sleep(time.Duration(delay) * time.Second)
							command.RestartWireguard(true, filepath)
						}

					}

					// 打印监听事件
					log.Infof("监听事件是：%s", event.String())
				case _, ok := <-watcher.Errors:
					if !ok {
						return
					}
				}
			}
		}()

		if err = watcher.Add(filepath); err != nil {
			log.Errorf("添加[%s]监听失败: %v", filepath, err.Error())
			return
		}
		<-done
	}()
}
