package service

import (
	"fmt"
	"strings"
	"time"

	"be.ems/src/framework/config"
	"be.ems/src/framework/logger"
	"be.ems/src/framework/utils/cmd"
	"be.ems/src/framework/utils/date"
	neService "be.ems/src/modules/network_element/service"
)

// 实例化服务层 TcpdumpImpl 结构体
var NewTcpdumpImpl = &TcpdumpImpl{
	neInfoService: neService.NewNeInfoImpl,
	tcpdumpPIDMap: map[string]string{},
}

// 信令抓包 服务层处理
type TcpdumpImpl struct {
	// 网元信息服务
	neInfoService neService.INeInfo
	// 抓包进程PID
	tcpdumpPIDMap map[string]string
}

// DumpStart 触发tcpdump开始抓包 filePcapName, err
func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) {
	// 检查网元信息
	neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
	if neInfo.NeId != neId {
		return "", fmt.Errorf("noData")
	}

	// SSH命令
	usernameNe := config.Get("ne.user").(string) // 网元统一用户
	sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP)

	// 是否拥有sudo权限并拼接
	withSudo := ""
	if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil {
		withSudo = "sudo "
	}

	if msg, err := cmd.ExecWithCheck("ssh", sshHost, fmt.Sprintf("%s tcpdump --version", withSudo)); err != nil {
		//  stderr: bash: tcpdump：未找到命令 => exit status 127
		msg = strings.TrimSpace(msg)
		logger.Warnf("DumpStart err: %s => %s", msg, err.Error())
		return "", fmt.Errorf(msg)
	}

	// 拼装命令
	neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
	timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS)
	fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID)
	sendCmd := fmt.Sprintf("cd /tmp \n %s nohup timeout 30m tcpdump -i any %s -s0 -w %s.pcap > %s.log 2>&1 & \necho $!", withSudo, cmdStr, fileName, fileName)
	// cd /tmp
	// sudo nohup timeout 60m tcpdump -i any -n -s 0 -v -w -s0 -w 20240115140822_UDM_001.pcap > 20240115140822_UDM_001.log 2>&1 & echo $!
	msg, err := cmd.ExecWithCheck("ssh", sshHost, sendCmd)
	msg = strings.TrimSpace(msg)
	if err != nil || strings.HasPrefix(msg, "stderr:") {
		logger.Warnf("DumpStart err: %s => %s", msg, err.Error())
		return "", err
	}

	// 检查进程 ps aux | grep tcpdump
	// 强杀 sudo pkill tcpdump
	s.tcpdumpPIDMap[neTypeID] = msg
	return fileName, err
}

// DumpStop 停止已存在抓包句柄
func (s *TcpdumpImpl) DumpStop(neType, neId, fileName string) (string, error) {
	// 检查网元信息
	neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
	if neInfo.NeId != neId {
		return "", fmt.Errorf("noData")
	}

	// SSH命令
	usernameNe := config.Get("ne.user").(string) // 网元统一用户
	sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP)

	// 是否拥有sudo权限并拼接
	withSudo := ""
	if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil {
		withSudo = "sudo "
	}

	// 是否存在进程
	neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
	pid, ok := s.tcpdumpPIDMap[neTypeID]
	if !ok || pid == "" {
		return "", fmt.Errorf("tcpdump is not running")
	}

	// 查看日志
	viewLogFile := ""
	if fileName != "" && strings.Contains(fileName, neTypeID) {
		viewLogFile = fmt.Sprintf("\n cat %s.log", fileName)
	}

	// 拼装命令
	sendCmd := fmt.Sprintf("cd /tmp \n %s kill %s %s", withSudo, pid, viewLogFile)
	msg, err := cmd.ExecWithCheck("ssh", sshHost, sendCmd)
	delete(s.tcpdumpPIDMap, neTypeID)
	if err != nil || strings.HasPrefix(msg, "stderr:") {
		logger.Warnf("DumpStop err: %s => %s", strings.TrimSpace(msg), err.Error())
		return "", err
	}
	return msg, nil
}

// DumpUPF UPF标准版抓包
func (s *TcpdumpImpl) DumpUPF(neType, neId, cmdStr string) (string, string, error) {
	// 检查网元信息
	neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
	if neInfo.NeId != neId {
		return "", "", fmt.Errorf("noData")
	}

	// SSH命令
	usernameNe := config.Get("ne.user").(string) // 网元统一用户
	sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP)

	// 是否拥有sudo权限并拼接
	withSudo := ""
	if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil {
		withSudo = "sudo "
	}

	if msg, err := cmd.ExecWithCheck("ssh", sshHost, fmt.Sprintf("%s expect -version", withSudo)); err != nil {
		//  stderr: bash: expect：未找到命令 => exit status 127
		msg = strings.TrimSpace(msg)
		logger.Warnf("DumpUPF err: %s => %s", msg, err.Error())
		return "", "", fmt.Errorf(msg)
	}

	// 拼装命令
	neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
	timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS)
	fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID)
	// UPF标准版本telnet脚本
	scriptStr := "set pcapCmd [lindex $argv 0]\nspawn telnet localhost 5002\nexpect \"upfd1# \"\nsend \"$pcapCmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\""
	writePcapFile := fmt.Sprintf("echo '%s' > pcapUPF.sh\n %s chmod +x pcapUPF.sh", scriptStr, withSudo)
	writeLogFile := fmt.Sprintf("> %s.log 2>&1 \ncat %s.log", fileName, fileName)

	// 以off结尾是停止抓包，不需要写文件
	pcapCmd := cmdStr
	if !strings.HasSuffix(pcapCmd, "off") {
		pcapCmd = fmt.Sprintf("%s file %s.pcap", cmdStr, fileName)
	}
	sendCmd := fmt.Sprintf("cd /tmp \n%s\n expect ./pcapUPF.sh '%s' %s", writePcapFile, pcapCmd, writeLogFile)
	// cd /tmp
	// echo '' >
	// expect ./cap.sh > pcapUPF.sh
	// sudo  chmod +x pcapUPF.sh
	// expect ./cap.sh 'pcap dispatch trace off' > 20240115165701_UDM_001.log 2>&1
	// cat 20240115165701_UDM_001.log
	msg, err := cmd.ExecWithCheck("ssh", sshHost, sendCmd)
	msg = strings.TrimSpace(msg)
	if err != nil || strings.HasPrefix(msg, "stderr:") {
		logger.Warnf("DumpUPF err: %s => %s", msg, err.Error())
		return "", "", err
	}
	if strings.Contains(msg, "Unable to connect to remote host") {
		return "", "", fmt.Errorf("connection refused")
	}
	// 以off结尾是停止抓包，不需要写文件
	if strings.HasSuffix(pcapCmd, "off") {
		if strings.Contains(msg, "Write ") {
			lastTmpIndex := strings.LastIndex(msg, "/tmp/")
			text := msg[lastTmpIndex+5:]
			extensionIndex := strings.LastIndex(text, ".pcap")
			if extensionIndex != -1 {
				fileName = text[:extensionIndex]
			}
		} else {
			fileName = ""
		}
	}
	return fileName, msg, err
}
