package api

import (
	"encoding/json"
	"fmt"
	"gitee.ltd/lxh/logger/log"
	"github.com/gin-gonic/gin"
	"github.com/spf13/cast"
	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
	"os"
	"strings"
	"wireguard-ui/component"
	"wireguard-ui/http/param"
	"wireguard-ui/http/response"
	"wireguard-ui/http/vo"
	"wireguard-ui/model"
	"wireguard-ui/script"
	"wireguard-ui/service"
	"wireguard-ui/utils"
)

type ClientApi struct{}

func Client() ClientApi {
	return ClientApi{}
}

// Save
// @description: 新增/编辑客户端
// @param c
func (ClientApi) Save(c *gin.Context) {
	var p param.SaveClient
	if err := c.ShouldBind(&p); err != nil {
		response.R(c).Validator(err)
		return
	}

	var loginUser *vo.User
	if loginUser = GetCurrentLoginUser(c); c.IsAborted() {
		return
	}
	if err := service.Client().SaveClient(p, loginUser); err != nil {
		response.R(c).FailedWithError(err)
		return
	}

	go func() {
		if err := script.New().GenerateConfig(); err != nil {
			log.Errorf("执行脚本失败")
		}
	}()

	response.R(c).OK()
}

// Delete
// @description: 删除客户端
// @receiver ClientApi
// @param c
func (ClientApi) Delete(c *gin.Context) {
	id := c.Param("id")
	if id == "" || id == "undefined" {
		response.R(c).FailedWithError("id不能为空")
		return
	}

	if err := service.Client().Delete(id); err != nil {
		response.R(c).FailedWithError(err)
		return
	}

	go func() {
		if err := script.New().GenerateConfig(); err != nil {
			log.Errorf("执行脚本失败")
		}
	}()

	response.R(c).OK()
}

// List
// @description: 客户端分页列表
// @receiver ClientApi
// @param c
func (ClientApi) List(c *gin.Context) {
	var p param.ClientList
	if err := c.ShouldBind(&p); err != nil {
		response.R(c).Validator(err)
		return
	}

	data, total, err := service.Client().List(p)
	if err != nil {
		response.R(c).FailedWithError(err)
		return
	}

	response.R(c).Paginate(data, total, p.Current, p.Size)
}

// GenerateKeys
// @description: 生成客户端密钥信息
// @receiver ClientApi
// @param c
func (ClientApi) GenerateKeys(c *gin.Context) {
	// 为空，新增
	privateKey, err := wgtypes.GeneratePrivateKey()
	if err != nil {
		response.R(c).FailedWithError(fmt.Errorf("生成密钥失败: %v", err.Error()))
		return
	}
	publicKey := privateKey.PublicKey().String()
	presharedKey, err := wgtypes.GenerateKey()
	if err != nil {
		response.R(c).FailedWithError(fmt.Errorf("生成密钥失败: %v", err.Error()))
		return
	}
	keys := vo.Keys{
		PrivateKey:   privateKey.String(),
		PublicKey:    publicKey,
		PresharedKey: presharedKey.String(),
	}

	response.R(c).OkWithData(keys)
}

// GenerateIP
// @description: 生成客户端IP
// @receiver ClientApi
// @param c
func (ClientApi) GenerateIP(c *gin.Context) {
	// 获取一下服务端信息，因为IP分配需要根据服务端的IP制定
	serverInfo, err := service.Setting().GetWGServerForConfig()
	if err != nil {
		response.R(c).FailedWithError("获取服务端信息失败")
		return
	}

	var assignIPS []string
	// 只获取最新的一个
	var clientInfo *model.Client
	if err = service.Client().Order("created_at DESC").Take(&clientInfo).Error; err == nil {
		// 遍历每一个ip是否可允许再分配
		for _, ip := range strings.Split(clientInfo.IpAllocation, ",") {
			if cast.ToInt64(utils.Network().GetIPSuffix(ip)) >= 255 {
				log.Errorf("IP：[%s]已无法分配新IP", ip)
				continue
			} else {
				assignIPS = append(assignIPS, ip)
			}
		}
	}

	ips := utils.Network().GenerateIPByIPS(serverInfo.Address, assignIPS...)

	clientIPS := ips
	serverIPS := serverInfo.Address
	response.R(c).OkWithData(map[string]any{
		"clientIPS": clientIPS,
		"serverIPS": serverIPS,
	})
}

// Download
// @description: 下载客户端配置文件
// @receiver ClientApi
// @param c
func (ClientApi) Download(c *gin.Context) {
	var id = c.Param("id")
	if id == "" || id == "undefined" {
		response.R(c).FailedWithError("id不能为空")
		return
	}
	var downloadType = c.Param("type")
	if downloadType == "" {
		response.R(c).FailedWithError("参数错误")
		return
	}

	data, err := service.Client().GetByID(id)
	if err != nil {
		response.R(c).FailedWithError("获取客户端信息失败")
		return
	}

	var keys vo.Keys
	_ = json.Unmarshal([]byte(data.Keys), &keys)

	globalSet, err := service.Setting().GetWGSetForConfig()
	if err != nil {
		response.R(c).FailedWithError("获取失败")
		return
	}

	serverConf, err := service.Setting().GetWGServerForConfig()
	if err != nil {
		response.R(c).FailedWithError("获取失败")
		return
	}

	outPath, err := component.Wireguard().GenerateClientFile(data, serverConf, globalSet)
	if err != nil {
		response.R(c).FailedWithError(fmt.Errorf("生成失败: %v", err.Error()))
		return
	}

	// 根据不同下载类型执行不同逻辑
	switch downloadType {
	case "QRCODE": // 二维码
		// 读取文件内容
		fileContent, err := os.ReadFile(outPath)
		if err != nil {
			response.R(c).FailedWithError("读取文件失败")
			return
		}

		png, err := utils.QRCode().GenerateQrCodeBase64(fileContent, 256)
		if err != nil {
			response.R(c).FailedWithError("生成二维码失败")
			return
		}

		if err = os.Remove(outPath); err != nil {
			log.Errorf("删除临时文件失败: %s", err.Error())
		}

		response.R(c).OkWithData(map[string]interface{}{
			"qrCode": png,
		})
	case "FILE": // 文件
		// 输出文件流
		c.Header("Content-Type", "application/octet-stream")
		c.Header("Content-Disposition", "attachment; filename="+outPath)
		c.Header("Content-Transfer-Encoding", "binary")
		c.Header("Connection", "keep-alive")
		c.File(outPath)
		if err = os.Remove(outPath); err != nil {
			log.Errorf("删除临时文件失败: %s", err.Error())
		}
	case "EMAIL": // 邮件
		if data.Email == "" {
			response.R(c).FailedWithError("当前客户端并未配置通知邮箱！")
			return
		}

		// 获取邮箱配置
		emailConf, err := service.Setting().GetByCode("EMAIL_SMTP")
		if err != nil {
			response.R(c).FailedWithError("获取邮箱配置失败，请先到设置页面的【其他】里面添加code为【EMAIL_SMTP】的具体配置")
			return
		}

		err = utils.Mail(emailConf).SendMail(data.Email, fmt.Sprintf("客户端: %s", data.Name), "请查收附件", outPath)
		if err != nil {
			response.R(c).FailedWithError("发送邮件失败")
			return
		}

		if err = os.Remove(outPath); err != nil {
			log.Errorf("删除临时文件失败: %s", err.Error())
		}
		response.R(c).OK()
	}

}
