package tui

import (
	"encoding/json"
	"fmt"
	"gitee.ltd/lxh/logger/log"
	"github.com/charmbracelet/bubbles/table"
	"github.com/spf13/cast"
	"os"
	"strconv"
	"strings"
	"time"
	"wireguard-ui/component"
	"wireguard-ui/global/constant"
	"wireguard-ui/http/param"
	"wireguard-ui/http/vo"
	"wireguard-ui/service"
	"wireguard-ui/utils"
)

type ClientComponent struct {
	LoginUser *vo.User
	Menu      []string
	Clients   [][]string
}

func NewClientComponent(loginUser *vo.User) *ClientComponent {
	ccp := &ClientComponent{
		LoginUser: loginUser,
		Menu:      []string{"[1] 客户端列表", "[2] 查看客户端配置", "[3] 添加客户端", "[4] 编辑客户端", "[5] 删除客户端", "[q] 返回上一级菜单"},
	}

	return ccp
}

// Menus
// @description: 客户端菜单
// @receiver c
func (c *ClientComponent) Menus() {
	fmt.Println("")
	for _, r := range c.Menu {
		fmt.Println(" -> " + r)
	}

	chooseMenu := readInput("\n请选择: ")
	switch chooseMenu {
	case "1":
		c.List(true)
	case "2":
		c.ShowConfig()
	case "3":
		c.Add()
	case "4":
		c.Edit()
	case "5":
		c.Delete()
	case "q":
		return
	}
}

// ConnectList
// @description: 客户端链接列表
// @receiver c
// @return string
func (c *ClientComponent) ConnectList() {
	fmt.Println("\n客户端链接列表")
	connectList, err := component.Wireguard().GetClients()
	if err != nil {
		fmt.Println("获取客户端链接列表失败: ", err.Error())
		return
	}

	var data [][]string
	for _, peer := range connectList {
		// 获取客户端链接信息
		clientInfo, err := service.Client().GetByPublicKey(peer.PublicKey.String())
		if err != nil {
			continue
		}
		var ipAllocation string
		for _, iaip := range peer.AllowedIPs {
			ipAllocation += iaip.String() + ","
		}
		// 去除一下最右边的逗号
		if len(ipAllocation) > 0 {
			ipAllocation = strings.TrimRight(ipAllocation, ",")
		}
		var isOnline = "否"
		if time.Since(peer.LastHandshakeTime).Minutes() < 3 {
			isOnline = "是"
		}
		data = append(data, []string{clientInfo.Name,
			clientInfo.Email,
			ipAllocation,
			isOnline,
			utils.FlowCalculation().Parse(peer.TransmitBytes),
			utils.FlowCalculation().Parse(peer.ReceiveBytes),
			peer.Endpoint.String(),
			peer.LastHandshakeTime.Format("2006-01-02 15:04:05")})
	}

	//if len(data) <= 0 {
	//	//data = append(data, []string{"暂无数据"})
	//	//	data = append(data, []string{"名称1", "12345678910@qq.com", "192.168.100.1", "是", "10G", "20G", "1.14.30.133:51280", "2024-12-20 15:07:36"}, []string{"名称2", "12345678910@qq.com", "192.168.100.2", "否", "20G", "40G", "1.14.30.133:51280", "2024-12-22 15:07:36"})
	//}

	title := []table.Column{
		{
			Title: "客户端名称",
			Width: 20,
		}, {
			Title: "联系邮箱",
			Width: 20,
		}, {
			Title: "分配的IP",
			Width: 30,
		}, {
			Title: "是否在线",
			Width: 10,
		}, {
			Title: "接收流量",
			Width: 10,
		}, {
			Title: "传输流量",
			Width: 10,
		}, {
			Title: "链接端点",
			Width: 30,
		}, {
			Title: "最后握手时间",
			Width: 30,
		}}

	Show(GenerateTable(title, data))
	return
}

// List
// @description: 客户端列表
// @receiver c
func (c *ClientComponent) List(showMenu bool) {

	title := []table.Column{
		{
			Title: "序号",
			Width: 5,
		},
		{
			Title: "ID",
			Width: 35,
		},
		{
			Title: "名称",
			Width: 25,
		},
		{
			Title: "联系邮箱",
			Width: 30,
		},
		{
			Title: "客户端IP",
			Width: 20,
		},
		{
			Title: "状态",
			Width: 10,
		},
		{
			Title: "离线通知",
			Width: 10,
		},
	}

	records, _, err := service.Client().List(param.ClientList{
		Page: param.Page{
			Current: -1,
		},
	})
	if err != nil {
		fmt.Println("获取客户端列表失败: ", err.Error())
		return
	}

	fmt.Println("\n客户端列表")
	var data [][]string
	for i, client := range records {
		var status, offlineNotify string
		if client.Enabled == 1 {
			status = "启用"
		} else {
			status = "禁用"
		}

		if client.OfflineMonitoring == 1 {
			offlineNotify = "开启"
		} else {
			offlineNotify = "关闭"
		}

		data = append(data, []string{
			cast.ToString(i + 1),
			client.Id,
			client.Name,
			client.Email,
			client.IpAllocationStr,
			status,
			offlineNotify,
		})
	}

	Show(GenerateTable(title, data))
	c.Clients = data
	if showMenu {
		c.Menus()
	}
	return
}

// ShowConfig
// @description: 显示配置
// @receiver c
func (c *ClientComponent) ShowConfig() {
	c.List(false)

	clientIdx := readInput("请输入客户端序号: ")
	downloadType := readInput("请输入下载类型(FILE - 文件 | EMAIL - 邮件): ")
	idx, err := strconv.Atoi(clientIdx)
	if err != nil {
		fmt.Println("输入有误: ", err.Error())
		return
	}

	if idx < 1 || idx > len(c.Clients) {
		fmt.Println("输入有误")
		return
	}

	client := c.Clients[idx-1]
	// 取到id
	clientID := client[1]
	// 查询客户端信息
	clientInfo, err := service.Client().GetByID(clientID)
	if err != nil {
		fmt.Println("获取客户端信息失败: ", err.Error())
		return
	}

	// 渲染配置
	var keys vo.Keys
	_ = json.Unmarshal([]byte(clientInfo.Keys), &keys)

	globalSet, err := service.Setting().GetWGSetForConfig()
	if err != nil {
		fmt.Println("获取全局配置失败: ", err.Error())
		return
	}

	serverConf, err := service.Setting().GetWGServerForConfig()
	if err != nil {
		fmt.Println("获取服务器配置失败: ", err.Error())
		return
	}

	outPath, err := component.Wireguard().GenerateClientFile(clientInfo, serverConf, globalSet)
	if err != nil {
		fmt.Println("生成客户端配置失败: ", err.Error())
		return
	}

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

		fmt.Println("\n#请将以下内容复制到客户端配置文件中【不包含本行提示语】")
		fmt.Println("\n" + string(fileContent))

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

	case "EMAIL": // 邮件
		if clientInfo.Email == "" {
			fmt.Println("当前客户端并未配置通知邮箱")
			return
		}

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

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

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

		fmt.Println("发送邮件成功，请注意查收！")
	}

	c.Menus()
}

// Add
// @description: 添加客户端
// @receiver c
func (c *ClientComponent) Add() {
	fmt.Println("\n添加客户端")
	clientIP, serverIP, err := GenerateIP()
	if err != nil {
		fmt.Println("生成客户端IP失败: ", err.Error())
		return
	}

	keys, err := GenerateKeys()
	if err != nil {
		fmt.Println("生成密钥对失败: ", err.Error())
		return
	}

	var p param.SaveClient
	p.Name = readInput("请输入客户端名称: ")
	p.Email = readInput("请输入联系邮箱: ")
	clientIPIn := readInput("请输入客户端IP(默认自动生成，多个采用 ',' 分割,例如 10.10.0.1/32,10.10.0.2/32): ")
	if clientIPIn == "" {
		p.IpAllocation = clientIP
	} else {
		p.IpAllocation = strings.Split(clientIPIn, ",")
	}

	p.AllowedIps = serverIP
	p.Keys = &param.Keys{
		PrivateKey:   keys.PrivateKey,
		PublicKey:    keys.PublicKey,
		PresharedKey: keys.PresharedKey,
	}

	var useServerDNS, enabled, offlineNotify constant.Status

	useServerDNSIn := readInput("是否使用服务器DNS(默认不使用 1 - 是 | 0 - 否 ): ")
	switch useServerDNSIn {
	case "1":
		useServerDNS = constant.Enabled
	case "0":
		useServerDNS = constant.Disabled
	default:
		useServerDNS = constant.Disabled
	}

	enabledIn := readInput("是否启用(默认启用 1 - 是 | 0 - 否 ): ")
	switch enabledIn {
	case "1":
		enabled = constant.Enabled
	case "0":
		enabled = constant.Disabled
	default:
		enabled = constant.Enabled
	}

	offlineNotifyIn := readInput("是否开启离线通知(默认关闭 1 - 是 | 0 - 否 ): ")
	switch offlineNotifyIn {
	case "1":
		offlineNotify = constant.Enabled
	case "0":
		offlineNotify = constant.Disabled
	default:
		offlineNotify = constant.Disabled
	}
	p.UseServerDns = &useServerDNS
	p.Enabled = &enabled
	p.OfflineMonitoring = &offlineNotify

	err = service.Client().SaveClient(p, c.LoginUser)
	if err != nil {
		fmt.Println("添加客户端失败: ", err.Error())
		return
	}

	fmt.Println("添加客户端成功")
	c.List(true)
	return
}

// Edit
// @description: 编辑客户端
// @receiver c
func (c *ClientComponent) Edit() {
	fmt.Println("\n编辑客户端")
	c.List(false)

	clientIdx := readInput("请输入客户端序号: ")
	idx, err := strconv.Atoi(clientIdx)
	if err != nil {
		fmt.Println("输入有误: ", err.Error())
		return
	}

	if idx < 1 || idx > len(c.Clients) {
		fmt.Println("输入有误")
		return
	}

	client := c.Clients[idx-1]
	// 取到id
	clientID := client[1]
	// 查询客户端信息
	clientInfo, err := service.Client().GetByID(clientID)
	if err != nil {
		fmt.Println("获取客户端信息失败: ", err.Error())
		return
	}

	var p param.SaveClient
	p.Id = clientID
	p.Name = readInput("请输入客户端名称[无需改变请回车跳过，下同]: ")
	p.Email = readInput("请输入联系邮箱: ")
	clientIPIn := readInput("请输入客户端IP(默认自动生成，多个采用 ',' 分割,例如 10.10.0.1/32,10.10.0.2/32): ")
	if clientIPIn == "" {
		p.IpAllocation = strings.Split(clientInfo.IpAllocation, ",")
	} else {
		p.IpAllocation = strings.Split(clientIPIn, ",")
	}

	p.AllowedIps = strings.Split(clientInfo.AllowedIps, ",")
	var keys *param.Keys
	_ = json.Unmarshal([]byte(clientInfo.Keys), &keys)

	p.Keys = keys

	var useServerDNS, enabled, offlineNotify constant.Status

	useServerDNSIn := readInput("是否使用服务器DNS(默认不使用 1 - 是 | 0 - 否 ): ")
	switch useServerDNSIn {
	case "1":
		useServerDNS = constant.Enabled
	case "0":
		useServerDNS = constant.Disabled
	default:
		useServerDNS = clientInfo.UseServerDns
	}

	enabledIn := readInput("是否启用(默认启用 1 - 是 | 0 - 否 ): ")
	switch enabledIn {
	case "1":
		enabled = constant.Enabled
	case "0":
		enabled = constant.Disabled
	default:
		enabled = clientInfo.Enabled
	}

	offlineNotifyIn := readInput("是否开启离线通知(默认关闭 1 - 是 | 0 - 否 ): ")
	switch offlineNotifyIn {
	case "1":
		offlineNotify = constant.Enabled
	case "0":
		offlineNotify = constant.Disabled
	default:
		offlineNotify = clientInfo.OfflineMonitoring
	}
	p.UseServerDns = &useServerDNS
	p.Enabled = &enabled
	p.OfflineMonitoring = &offlineNotify

	err = service.Client().SaveClient(p, c.LoginUser)
	if err != nil {
		fmt.Println("编辑客户端失败: ", err.Error())
		return
	}

	fmt.Println("编辑客户端成功")
	c.List(true)
	return
}

// Delete
// @description: 删除客户端
// @receiver c
func (c *ClientComponent) Delete() {
	fmt.Println("\n删除客户端")

	c.List(false)

	clientIdx := readInput("请输入客户端序号: ")
	idx, err := strconv.Atoi(clientIdx)
	if err != nil {
		fmt.Println("输入有误: ", err.Error())
		return
	}

	if idx < 1 || idx > len(c.Clients) {
		fmt.Println("输入有误")
		return
	}

	client := c.Clients[idx-1]
	// 取到id
	clientID := client[1]
	if err := service.Client().Delete(clientID); err != nil {
		fmt.Println("删除客户端失败: ", err.Error())
		return
	}

	c.List(true)
	fmt.Println("删除客户端成功")
	return
}
