package service

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"ems.agt/features/firewall/model"
	"ems.agt/features/firewall/repo"
	"ems.agt/lib/core/cmd"
	"ems.agt/lib/core/utils/firewall"
	fireClient "ems.agt/lib/core/utils/firewall/client"
	"ems.agt/lib/core/utils/scan"
)

// 实例化服务层 ServiceFirewall 结构体
var NewServiceFirewall = &ServiceFirewall{
	repoFirewall: *repo.NewRepoFirewall,
}

// ServiceFirewall 防火墙 服务层处理
type ServiceFirewall struct {
	repoFirewall repo.RepoFirewall
}

// LoadBaseInfo 获取防火墙基础信息
func (s *ServiceFirewall) LoadBaseInfo() (model.FirewallBaseInfo, error) {
	var baseInfo model.FirewallBaseInfo
	baseInfo.PingStatus = s.pingStatus()
	baseInfo.Status = "not running"
	baseInfo.Version = "-"
	baseInfo.Name = "-"
	client, err := firewall.NewFirewallClient()
	if err != nil {
		if err.Error() == "no such type" {
			return baseInfo, nil
		}
		return baseInfo, err
	}
	baseInfo.Name = client.Name()
	baseInfo.Status, err = client.Status()
	if err != nil {
		return baseInfo, err
	}
	if baseInfo.Status == "not running" {
		return baseInfo, err
	}
	baseInfo.Version, err = client.Version()
	if err != nil {
		return baseInfo, err
	}
	return baseInfo, nil
}

// LoadBaseInfo 获取防火墙基础信息
func (s *ServiceFirewall) RulePage(querys model.RuleQuerys) (map[string]any, error) {
	var (
		datas     []fireClient.FireInfo
		backDatas []fireClient.FireInfo
	)

	data := map[string]any{
		"total": 0,
		"rows":  backDatas,
	}

	client, err := firewall.NewFirewallClient()
	if err != nil {
		return data, err
	}
	if querys.Type == "port" {
		ports, err := client.ListPort()
		if err != nil {
			return data, err
		}
		if len(querys.Info) != 0 {
			for _, port := range ports {
				if strings.Contains(port.Port, querys.Info) {
					datas = append(datas, port)
				}
			}
		} else {
			datas = ports
		}
	} else {
		addrs, err := client.ListAddress()
		if err != nil {
			return data, err
		}
		if len(querys.Info) != 0 {
			for _, addr := range addrs {
				if strings.Contains(addr.Address, querys.Info) {
					datas = append(datas, addr)
				}
			}
		} else {
			datas = addrs
		}
	}

	var datasFilterStatus []fireClient.FireInfo
	if len(querys.Status) != 0 {
		for _, data := range datas {
			portItem, _ := strconv.Atoi(data.Port)
			if querys.Status == "free" && !scan.ScanPortWithProto(portItem, data.Protocol) {
				datasFilterStatus = append(datasFilterStatus, data)
			}
			if querys.Status == "used" && scan.ScanPortWithProto(portItem, data.Protocol) {
				datasFilterStatus = append(datasFilterStatus, data)
			}
		}
	} else {
		datasFilterStatus = datas
	}
	var datasFilterStrategy []fireClient.FireInfo
	if len(querys.Strategy) != 0 {
		for _, data := range datasFilterStatus {
			if querys.Strategy == data.Strategy {
				datasFilterStrategy = append(datasFilterStrategy, data)
			}
		}
	} else {
		datasFilterStrategy = datasFilterStatus
	}

	total, start, end := len(datasFilterStrategy), (querys.PageNum-1)*querys.PageSize, querys.PageNum*querys.PageSize
	if start > total {
		backDatas = make([]fireClient.FireInfo, 0)
	} else {
		if end >= total {
			end = total
		}
		backDatas = datasFilterStrategy[start:end]
	}

	datasFromDB := s.repoFirewall.List(model.Firewall{})
	for i := 0; i < len(backDatas); i++ {
		for _, des := range datasFromDB {
			if querys.Type != des.Type {
				continue
			}
			if backDatas[i].Port == des.Port && querys.Type == "port" &&
				backDatas[i].Protocol == des.Protocol &&
				backDatas[i].Strategy == des.Strategy &&
				backDatas[i].Address == des.Address {
				backDatas[i].Description = des.Description
				break
			}
			if querys.Type == "address" && backDatas[i].Strategy == des.Strategy && backDatas[i].Address == des.Address {
				backDatas[i].Description = des.Description
				break
			}
		}
	}

	if querys.Type == "port" {
		for i := 0; i < len(backDatas); i++ {
			port, _ := strconv.Atoi(backDatas[i].Port)
			backDatas[i].IsUsed = scan.ScanPort(port)
			if backDatas[i].Protocol == "udp" {
				backDatas[i].IsUsed = scan.ScanUDPPort(port)
				continue
			}
		}
	}
	go s.cleanUnUsedData(client)

	return data, nil
}

func (s *ServiceFirewall) pingStatus() string {
	if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
		return "None"
	}
	sudo := cmd.SudoHandleCmd()
	command := fmt.Sprintf("%s cat /etc/sysctl.conf | grep net/ipv4/icmp_echo_ignore_all= ", sudo)
	stdout, _ := cmd.Exec(command)
	if stdout == "net/ipv4/icmp_echo_ignore_all=1\n" {
		return "Enable"
	}
	return "Disable"
}

func (s *ServiceFirewall) cleanUnUsedData(client firewall.FirewallClient) {
	list, _ := client.ListPort()
	addressList, _ := client.ListAddress()
	list = append(list, addressList...)
	if len(list) == 0 {
		return
	}
	records := s.repoFirewall.List(model.Firewall{})
	if len(records) == 0 {
		return
	}
	for _, item := range list {
		for i := 0; i < len(records); i++ {
			if records[i].Port == item.Port && records[i].Protocol == item.Protocol && records[i].Strategy == item.Strategy && records[i].Address == item.Address {
				records = append(records[:i], records[i+1:]...)
			}
		}
	}

	for _, record := range records {
		_ = s.repoFirewall.Delete(record.ID)
	}
}
