package ebpftracer

import (
	"debug/elf"
	"errors"
	"fmt"
	"log"
	"os"

	"github.com/cilium/ebpf/link"
	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/aotinject"
	. "github.com/coroot/coroot-node-agent/utils/modelse"
	klog "github.com/sirupsen/logrus"
)

func (t *Tracer) AttachJavaAotNioReadUprobes(pid uint32, codeType CodeType, rootfs string) ([]link.Link, error) {
	if t.DisableL7Tracing() {
		return nil, nil
	}
	var links []link.Link
	var bpath string

	// 根据进程 pid 获取进程的可执行文件路径
	bpath, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
	if err != nil {
		log.Fatalf("Failed to read proc path: %v", err)
	}
	fmt.Printf("procPath: %s\n", bpath)
	//bpath = rootfs + bpath
	klog.Infof("[attach] find the nio.so path is  %s", bpath)

	readInfo, _ := aotinject.GetProcessFunctionInfo(int(pid), bpath, symbolsocketRead0)
	if readInfo == nil {
		return nil, fmt.Errorf("can not find %s", symbolsocketRead0)
	}
	// 打印 readInfo
	klog.Infof("[attach] AttachJavaAotNioReadUprobes readInfo %v", readInfo)

	ef, err := elf.Open(bpath)
	if err != nil {
		klog.Errorf("[attach] open elf: %v", err)
		return nil, err
	}
	defer ef.Close()

	textSection := ef.Section(".text")
	if textSection == nil {
		return nil, errors.New("can not find .text section")
	}
	textSectionData, err := textSection.Data()
	if err != nil {
		return nil, err
	}
	textSectionLen := uint64(len(textSectionData) - 1)
	// 打印 textSectionLen
	klog.Infof("[attach] AttachJavaAotNioReadUprobes textSectionLen %d{0x%x}", textSectionLen, textSection.Addr)

	sStart := readInfo.Offset - textSection.Addr
	sEnd := sStart + readInfo.Size
	if sEnd > textSectionLen {
		klog.Infof("[attach] AttachJavaAotNioReadUprobes sEnd > textSectionLen %d, %d", textSectionLen, sEnd)
		return nil, fmt.Errorf("can not find %s", symbolsocketRead0)
	}
	sBytes := textSectionData[sStart:sEnd]
	// 打印 sBytes
	returnOffsets := getCallNextMoveOffsets(ef.Machine, sBytes)
	if len(returnOffsets) == 0 {
		return nil, fmt.Errorf("failed to find call in uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0")
	}
	klog.Infof("[attach] java symbol offset is  %v", returnOffsets)
	for _, offset := range returnOffsets {
		klog.Infof("[attach] java symbol offset is  %d", offset)
		ex, err := link.OpenExecutable(bpath)
		if err != nil {
			klog.Errorf("[attach] open executable: %v", err)
			return nil, err
		}
		l, err := ex.Uprobe(readInfo.Name, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &link.UprobeOptions{Address: readInfo.Offset, Offset: uint64(offset), PID: int(pid)})
		if err != nil {
			return nil, fmt.Errorf("failed to attach uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0 uprobe")
		}

		links = append(links, l)
	}

	if len(links) == 0 {
		return nil, fmt.Errorf("no links found for %s", bpath)
	}
	klog.WithField("pid", pid).Infof("[attach] libnio attached!")
	return links, nil
}

func (t *Tracer) AttachJavaAotNetWriteUprobes(pid uint32, rootfs string) ([]link.Link, error) {
	if t.DisableL7Tracing() {
		return nil, nil
	}

	// 根据进程 pid 获取进程的可执行文件路径
	procPath, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
	if err != nil {
		log.Fatalf("Failed to read proc path: %v", err)
	}
	fmt.Printf("procPath: %s\n", procPath)

	sendFunctionName := "NET_Send"
	injectFunctionName := "Java_java_net_SocketOutputStream_socketWrite0"

	// NETCoreAOTInject
	aotInjector := aotinject.AOTInjector{
		PID:                 int(pid),
		SendFunctionLibPath: procPath,
		SendFunctionName:    sendFunctionName,
		InjectLibPath:       procPath,
		InjectFunctionName:  injectFunctionName,
		CWLibName:           "libmylib.so",
		CWFunctionName:      "asmnop",
		CWLibPath:           "/data/roger/ebpfdemo/mylib/libmylib.so",
	}
	// 打印 aotInjector
	fmt.Printf("aotInjector: %v\n", aotInjector)
	hookOffset, err := aotinject.AotInject(aotInjector)
	// 打印 hookOffset
	fmt.Printf("hookOffset: %d\n", hookOffset)

	if (hookOffset == 0) || (err != nil) {
		fmt.Println("failed to inject SystemNative_Send", err)
		return nil, err
	}

	var links []link.Link
	ex, err := link.OpenExecutable(aotInjector.CWLibPath)
	if err != nil {
		return nil, err
	}
	opt := link.UprobeOptions{
		Offset: uint64(hookOffset),
		PID:    int(pid),
	}
	upread02, err := ex.Uprobe(aotInjector.CWFunctionName, t.uprobes["javaaot_asmnop"], &opt)
	if err != nil {
		return nil, err
	}
	links = append(links, upread02)

	if len(links) == 0 {
		return nil, nil
	}
	fmt.Println("netcore client uprobes attached", pid)

	return links, nil
}
