package packet_task

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"be.ems/src/framework/logger"
	wsService "be.ems/src/modules/ws/service"
	"github.com/gopacket/gopacket"
	"github.com/gopacket/gopacket/layers"
	"github.com/gopacket/gopacket/pcap"
	"github.com/gopacket/gopacket/pcapgo"
)

// 捕获任务
var taskMap sync.Map

// task  任务信息
type task struct {
	TaskNo string         // 任务编号
	Handle *pcap.Handle   // 捕获句柄
	File   *os.File       // 捕获信息输出文件句柄
	Writer *pcapgo.Writer // 捕获信息输出句柄
	Filter string         // 过滤表达式
	Ticker *time.Ticker   // 任务失效定时器
}

// NetworkDevices 获取网卡设备信息
func NetworkDevices() ([]pcap.Interface, error) {
	devices, err := pcap.FindAllDevs()
	if err != nil {
		logger.Errorf("interfaces find all devices err: %s", err.Error())
		return nil, err
	}
	return devices, nil
}

// verifyDevice 检查网卡设备是否存在
func verifyDevice(str string) (string, bool) {
	devices, err := pcap.FindAllDevs()
	if err != nil {
		logger.Errorf("interfaces find all devices err: %s", err.Error())
		return "", false
	}
	for _, device := range devices {
		if len(device.Addresses) == 0 {
			continue
		}
		if device.Name == str {
			return device.Name, true
		}
		for _, address := range device.Addresses {
			if address.IP.String() == str {
				return device.Name, true
			}
		}
	}
	return "", false
}

// outputPCAP 输出 pcap 文件
// 新文件时需要 snaplen  最大长度 linktype 链路类型
func outputPCAP(snaplen uint32, linktype layers.LinkType, outputFile string) (*os.File, *pcapgo.Writer, error) {
	var err error
	var f *os.File
	if err := os.MkdirAll(filepath.Dir(outputFile), 0775); err != nil {
		return nil, nil, err
	}

	// 检查文件是否存在
	if _, err = os.Stat(outputFile); os.IsNotExist(err) {
		f, err = os.Create(outputFile)
		if err != nil {
			return nil, nil, err
		}
		w := pcapgo.NewWriter(f)
		w.WriteFileHeader(snaplen, linktype) // new file, must do this.
		return f, w, nil
	}

	f, err = os.OpenFile(outputFile, os.O_APPEND, 0700)
	if err != nil {
		return nil, nil, err
	}
	w := pcapgo.NewWriter(f)
	return f, w, nil
}

// capturePacketSource 捕获数据
func capturePacketSource(taskInfo *task) {
	// capture packets
	packetSource := gopacket.NewPacketSource(taskInfo.Handle, taskInfo.Handle.LinkType())
	packetSource.Lazy = false
	packetSource.NoCopy = true
	packetSource.DecodeStreamsAsDatagrams = true

	// 协程停止后关闭句柄并移除任务信息
	defer func() {
		taskInfo.Ticker.Stop()
		taskInfo.Handle.Close()
		if taskInfo.File != nil {
			taskInfo.File.Close()
		}
		taskMap.Delete(taskInfo.TaskNo)
	}()

	frameNumber := 0             // 帧编号
	frameTime := 0.000000        // 时间
	var startTimestamp time.Time // 开始时间

	for {
		select {
		case <-taskInfo.Ticker.C:
			return
		case packet := <-packetSource.Packets():
			if packet == nil {
				continue
			}
			// if packet.Metadata().Timestamp.Before(time.Now()) {
			// 	continue
			// }
			if taskInfo.Writer != nil {
				taskInfo.Writer.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
			}
			fmt.Println("----------  packet.Layers() ", len(packet.Layers()))
			frameNumber++ // 帧编号
			currentTimestamp := packet.Metadata().Timestamp
			if !startTimestamp.IsZero() {
				// 计算时间差转换为秒
				frameTime = currentTimestamp.Sub(startTimestamp).Seconds()
			} else {
				startTimestamp = currentTimestamp
			}

			// 数据
			frameMeta := parsePacketFrame(frameNumber, frameTime, packet)

			// 推送到ws订阅组
			wsService.NewWSSend.ByGroupID(fmt.Sprintf("%s%s", wsService.GROUP_TRACE_PACKET, taskInfo.TaskNo), frameMeta)
		}
	}
}

// LiveStart 开始捕获数据
func LiveStart(taskNo, deviceName, filterBPF string, outputFile bool) (string, error) {
	if _, ok := taskMap.Load(taskNo); ok {
		return "", fmt.Errorf("task no. %s already exist", taskNo)
	}

	// Verify the specified network interface exists
	device, deviceOk := verifyDevice(deviceName)
	if !deviceOk {
		return "", fmt.Errorf("network device not exist: %s", deviceName)
	}

	snapshotLength := 262144

	// open device
	handle, err := pcap.OpenLive(device, int32(snapshotLength), true, pcap.BlockForever)
	if err != nil {
		logger.Errorf("open live err: %s", err.Error())
		if strings.Contains(err.Error(), "operation not permitted") {
			return "", fmt.Errorf("you don't have permission to capture on that/these device(s)")
		}
		return "", err
	}

	// write a new file
	var w *pcapgo.Writer
	var f *os.File
	if outputFile {
		// 网管本地路径
		localFilePath := fmt.Sprintf("/tmp/omc/packet/%s.pcap", taskNo)
		if runtime.GOOS == "windows" {
			localFilePath = fmt.Sprintf("C:%s", localFilePath)
		}
		f, w, err = outputPCAP(uint32(snapshotLength), handle.LinkType(), localFilePath)
		if err != nil {
			return "", err
		}
	}

	// set filter
	if filterBPF != "" {
		if err = handle.SetBPFFilter(filterBPF); err != nil {
			logger.Errorf("packet BPF Filter %s => %s", filterBPF, err.Error())
			filterBPF = ""
		}
	}

	// save tasks
	taskInfo := &task{
		TaskNo: taskNo,
		Handle: handle,
		File:   f,
		Writer: w,
		Filter: filterBPF,
		Ticker: time.NewTicker(time.Second * 120),
	}

	go capturePacketSource(taskInfo)
	taskMap.Store(taskNo, taskInfo)
	return fmt.Sprintf("task no. %s initiated", taskNo), nil
}

// LiveFilter 捕获过滤
func LiveFilter(taskNo, expr string) error {
	info, ok := taskMap.Load(taskNo)
	if !ok {
		return fmt.Errorf("task no. %s not exist", taskNo)
	}
	task := info.(*task)
	task.Filter = expr
	err := task.Handle.SetBPFFilter(expr)
	if err != nil {
		logger.Errorf("packet BPF Filter %s => %s", expr, err.Error())
		return fmt.Errorf("can't parse filter expression")
	}
	return nil
}

// LiveTimeout 更新捕获失效时间
func LiveTimeout(taskNo string, seconds int) error {
	info, ok := taskMap.Load(taskNo)
	if !ok {
		return fmt.Errorf("task no. %s not exist", taskNo)
	}
	info.(*task).Ticker.Reset(time.Duration(seconds) * time.Second)
	return nil
}

// LiveStop 停止捕获数据
func LiveStop(taskNo string) error {
	info, ok := taskMap.Load(taskNo)
	if !ok {
		return fmt.Errorf("task no. %s not exist", taskNo)
	}
	info.(*task).Ticker.Reset(time.Millisecond)
	return nil
}
