package service

import (
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"

	"be.ems/src/framework/cmd"
	"be.ems/src/framework/logger"
	neService "be.ems/src/modules/ne/service"
)

// 实例化服务层 TCPdump 结构体
var NewTCPdump = &TCPdump{
	neInfoService: neService.NewNeInfo,
}

// 信令抓包 服务层处理
type TCPdump struct {
	neInfoService *neService.NeInfo // 网元信息服务
}

// 抓包进程PID
var dumpPIDMap sync.Map

// DumpStart 触发tcpdump开始抓包
func (s *TCPdump) DumpStart(neType, neId, cmdStr string) (string, error) {
	// 命令检查
	if strings.Contains(cmdStr, "-w") {
		return "", fmt.Errorf("command cannot contain -w")
	}

	// // 查询网元获取IP
	// neInfo := s.neInfoService.FindByNeTypeAndNeID(neType, neId)
	// if neInfo.NeId != neId || neInfo.IP == "" {
	// 	return "", fmt.Errorf("app.common.noNEInfo")
	// }
	// // 网元主机的SSH客户端
	// sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
	// if err != nil {
	// 	return "", err
	// }
	// defer sshClient.Close()

	// 检查是否安装tcpdump
	if msg, err := cmd.Exec("sudo tcpdump --version"); err != nil {
		// bash: tcpdump: command not found
		msg = strings.TrimSpace(msg)
		logger.Errorf("DumpStart err: %s => %s", msg, err.Error())
		return "", fmt.Errorf("%s", msg)
	}

	taskCode := time.Now().Format("20060102150405")
	// 存放文件目录 /usr/local/omc/tcpdump/udm/001/20240817104241
	neDirTemp := fmt.Sprintf("/usr/local/omc/tcpdump/%s/%s/%s", strings.ToLower(neType), neId, taskCode)
	cmd.Exec(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 777 -R /usr/local/omc/tcpdump", neDirTemp))

	// 命令拼装
	logPath := fmt.Sprintf("%s/tcpdump.log", neDirTemp)
	filePath := fmt.Sprintf("%s/part_%s.pcap ", neDirTemp, taskCode)
	if strings.Contains(cmdStr, "-G") {
		filePath = fmt.Sprintf("%s/part_%%Y%%m%%d%%H%%M%%S.pcap ", neDirTemp)
	}
	cmd.Exec(fmt.Sprintf("sudo touch %s && sudo chmod o+w %s", logPath, logPath))
	sendCmd := fmt.Sprintf("sudo timeout 60m sudo tcpdump -i any %s -w %s > %s 2>&1 & echo $!", cmdStr, filePath, logPath)
	// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -G 60 -W 6 -w /usr/local/omc/tcpdump/udm/001/20240817104241/part_%Y-%m-%d_%H:%M:%S.pcap  > /usr/local/omc/tcpdump/udm/001/20240817104241/tcpdump.log 2>&1 & echo $!
	// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -w /usr/local/omc/tcpdump/udm/001/20240817105440/part_2024-08-17_10:54:40.pcap  > /usr/local/omc/tcpdump/udm/001/20240817105440/tcpdump.log 2>&1 & echo $!
	//
	// timeout 超时60分钟后发送kill命令，1分钟后强制终止命令。tcpdump -G 文件轮转间隔时间（秒）  -W 文件轮转保留最近数量
	// sudo timeout --kill-after=1m 60m sudo tcpdump -i any -n -s 0 -v -G 10 -W 7 -w /tmp/part_%Y%m%d%H%M%S.pcap > /tmp/part.log 2>&1 & echo $!
	// sudo kill $(pgrep -P 722729)
	outputPID, err := cmd.Exec(sendCmd)
	outputPID = strings.TrimSpace(outputPID)
	if err != nil || strings.HasPrefix(outputPID, "stderr:") {
		logger.Errorf("DumpStart err: %s => %s", outputPID, err.Error())
		return "", err
	}

	// 日志文件行号
	PIDMap := s.logFileLastLine(neType)
	PIDMap["neType"] = neType
	PIDMap["neId"] = neId
	PIDMap["taskCode"] = taskCode
	PIDMap["pid"] = outputPID
	PIDMap["cmd"] = sendCmd

	// 检查进程 ps aux | grep tcpdump
	// 强杀 sudo pkill tcpdump
	pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neType), neId, taskCode)
	dumpPIDMap.Store(pidKey, PIDMap)
	return taskCode, err
}

// DumpStop 停止已存在抓包句柄
func (s *TCPdump) DumpStop(neType, neId, taskCode string) ([]string, error) {
	// // 查询网元获取IP
	// neInfo := s.neInfoService.FindByNeTypeAndNeID(neType, neId)
	// if neInfo.NeId != neId || neInfo.IP == "" {
	// 	return []string{}, fmt.Errorf("app.common.noNEInfo")
	// }
	// // 网元主机的SSH客户端
	// sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
	// if err != nil {
	// 	return []string{}, err
	// }
	// defer sshClient.Close()

	// 是否存在执行过的进程
	pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neType), neId, taskCode)
	PIDMap, ok := dumpPIDMap.Load(pidKey)
	if !ok || PIDMap == nil {
		return []string{}, fmt.Errorf("tcpdump is not running")
	}
	pid, ok := PIDMap.(map[string]string)["pid"]
	if !ok || pid == "" {
		return []string{}, fmt.Errorf("tcpdump is not running")
	}
	s.logFileLastLineToFile(PIDMap.(map[string]string))

	// 存放文件目录 /usr/local/omc/tcpdump/udm/001/20240817104241
	neDirTemp := fmt.Sprintf("/usr/local/omc/tcpdump/%s/%s/%s", strings.ToLower(neType), neId, taskCode)
	// 命令拼装
	sendCmd := fmt.Sprintf("pids=$(pgrep -P %s) && [ -n \"$pids\" ] && sudo kill $pids;sudo timeout 2s ls %s", pid, neDirTemp)
	// pids=$(pgrep -P 1914341) && [ -n "$pids" ] && sudo kill $pids;sudo timeout 2s ls /usr/local/omc/tcpdump/udm/001/20240817104241
	output, err := cmd.Exec(sendCmd)
	output = strings.TrimSpace(output)
	if err != nil || strings.HasPrefix(output, "ls: ") {
		logger.Errorf("DumpStop err: %s => %s", output, err.Error())
		return []string{}, err
	}
	files := strings.Split(output, "\n")
	dumpPIDMap.Delete(pidKey)
	return files, nil
}

// logFileLastLine 日志文件最后行号
func (s *TCPdump) logFileLastLine(neType string) map[string]string {
	logFileArr := make([]string, 0)
	mapFile := make(map[string]string, 0)

	// 存放文件目录 /var/log/xxx.log
	if neType == "IMS" {
		logFileArr = append(logFileArr,
			"/var/log/ims/pcscf/pcscf.log",
			"/var/log/ims/bgcf/bgcf.log",
			"/var/log/ims/bsf/bsf.log",
			"/var/log/ims/icscf/icscf.log",
			"/var/log/ims/ismc/ismc.log",
			"/var/log/ims/mmtel/mmtel.log",
			"/var/log/ims/scscf/scscf.log",
			"/var/log/ims/iwf/iwf.log",
		)
	} else {
		neLogFile := fmt.Sprintf("/var/log/%s.log", strings.ToLower(neType))
		logFileArr = append(logFileArr, neLogFile)
	}

	for _, v := range logFileArr {
		// lastLine, err := sshClient.RunCMD(fmt.Sprintf("sudo sed -n '$=' %s", v))
		lastLine, err := cmd.Exec(fmt.Sprintf("sudo sed -n '$=' %s", v))
		lastLine = strings.TrimSpace(lastLine)
		if err != nil || strings.HasPrefix(lastLine, "sed: can't") {
			logger.Errorf("logFileLastLine err: %s => %s", lastLine, err.Error())
			continue
		}
		mapFile[v] = lastLine
	}
	return mapFile
}

// logFileLastLine 日志文件最后行号
func (s *TCPdump) logFileLastLineToFile(PIDMap map[string]string) error {
	// // 网元主机的SSH客户端进行文件传输
	// sftpClient, err := sshClient.NewClientSFTP()
	// if err != nil {
	// 	return fmt.Errorf("ne info sftp client err")
	// }
	// defer sftpClient.Close()

	neType := PIDMap["neType"]
	neId := PIDMap["neId"]
	taskCode := PIDMap["taskCode"]
	// 存放文件目录 /usr/local/omc/tcpdump/udm/001/20240817104241
	neDirTemp := fmt.Sprintf("/usr/local/omc/tcpdump/%s/%s/%s", strings.ToLower(neType), neId, taskCode)
	// sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 777 -R /usr/local/omc/tcpdump", neDirTemp))
	cmd.Exec(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 777 -R /usr/local/omc/tcpdump", neDirTemp))

	lastLineMap := s.logFileLastLine(neType)
	for lastLogFile, lastFileLine := range lastLineMap {
		for startLogFile, startFileLine := range PIDMap {
			if lastLogFile == startLogFile && lastFileLine != "" {
				if startFileLine == "" {
					startFileLine = "1" // 起始行号从第一行开始
				}
				outputFile := fmt.Sprintf("%s/%s", neDirTemp, filepath.Base(lastLogFile))
				// sendCmd := fmt.Sprintf("sudo sed -n \"%s,%sp\" \"%s\" | sudo tee \"%s\" > /dev/null", startFileLine, lastFileLine, lastLogFile, outputFile)
				// sudo sed -n "1,5p" "/var/log/amf.log" | sudo tee "/usr/local/omc/tcpdump/amf/001/20241008141336/amf.log" > /dev/null
				// output, err := sshClient.RunCMD(sendCmd)
				sendCmd := fmt.Sprintf("sudo sed -n \"%s,%sp\" \"%s\" | sudo tee \"%s\" > /dev/null", startFileLine, lastFileLine, lastLogFile, outputFile)
				output, err := cmd.Exec(sendCmd)
				if err != nil || strings.HasPrefix(output, "stderr:") {
					logger.Errorf("logFileLastLineToFile err: %s => %s", strings.TrimSpace(output), err.Error())
					continue
				}
			}
		}
	}
	return nil
}

// UPFTrace UPF标准版内部抓包
func (s *TCPdump) UPFTrace(neType, neId, cmdStr string) (string, error) {
	// 命令检查
	if strings.Contains(cmdStr, "file") {
		return "", fmt.Errorf("command cannot contain file")
	}

	// 查询网元获取IP
	neInfo := s.neInfoService.FindByNeTypeAndNeID(neType, neId)
	if neInfo.NeId != neId || neInfo.IP == "" {
		return "", fmt.Errorf("app.common.noNEInfo")
	}
	// 网元主机的SSH客户端
	sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
	if err != nil {
		return "", err
	}
	defer sshClient.Close()
	// 网元主机的Telnet客户端
	telnetClient, err := s.neInfoService.NeRunTelnetClient("UPF", neInfo.NeId, 2)
	if err != nil {
		return "", err
	}
	defer telnetClient.Close()
	telnetClient.RunCMD("") // 再次排空信息

	// 命令拼装
	fileName := fmt.Sprintf("%s_%s_part_%s.pcap ", neInfo.NeType, neInfo.NeId, time.Now().Format("20060102150405"))
	pcapCmd := fmt.Sprintf("%s\r\n", cmdStr)
	// 以off结尾是停止抓包，不需要写文件
	if !strings.Contains(cmdStr, "off") {
		// pcap trace rx tx max 100000 intfc any file UPF_001_part_20240817164516.pcap
		pcapCmd = fmt.Sprintf("%s file %s\r\n", cmdStr, fileName)
	}
	// 发送命令 UPF内部默认输出路径/tmp只能写文件名
	// pcap trace rx tx max 100000 intfc any file upf_test.pcap
	// pcap trace rx tx off
	output, err := telnetClient.RunCMD(pcapCmd)
	if err != nil {
		logger.Warnf("DumpUPF err: %s => %s", output, err.Error())
		return "", err
	}

	// 结果截取
	arr := strings.Split(output, "\r\n")
	if len(arr) < 2 {
		return "", fmt.Errorf("trace pacp run failed")
	}
	if len(arr) == 3 {
		resMsg := arr[1]
		// pcap trace: unknown input `f file UPF_001_part_2024-08-19...'
		// pcap trace: dispatch trace already enabled...
		// pcap trace: dispatch trace already disabled...
		// pcap trace: No packets captured...
		// Write 100000 packets to /tmp/UPF_001_part_20240817164516.pcap, and stop capture...
		if strings.Contains(resMsg, "unknown input") {
			return "", fmt.Errorf("trace pacp unknown input")
		}
		if strings.Contains(resMsg, "already enabled") {
			return "", fmt.Errorf("trace pacp already running")
		}
		if strings.Contains(resMsg, "already disabled") {
			return "", fmt.Errorf("trace pacp not running")
		}
		if strings.Contains(resMsg, "No packets") {
			return "", fmt.Errorf("trace pacp not packets")
		}
		if strings.Contains(resMsg, "packets to") {
			matches := regexp.MustCompile(`(/tmp/[^,\s]+)`).FindStringSubmatch(resMsg)
			if len(matches) == 0 {
				return "", fmt.Errorf("file path not found")
			}
			return matches[0], nil
		}
	}
	return "trace pacp running", nil
}
