package logic

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"sync"

	"github.com/alphadose/haxmap"
	"github.com/elastic/go-libaudit/v2/aucoalesce"
	"github.com/elastic/go-libaudit/v2/auparse"
)

var (
	// 接收审计事件的管道
	EventChan = make(chan *aucoalesce.Event, 1024)

	// 记录有效事件的map
	EventMap = haxmap.New[string, []*TaggedEvent]()

	// 记录需要注意的可执行文件路径
	ExecPath = map[string]bool{
		"/usr/bin/scp":                     true,
		"/usr/libexec/openssh/sftp-server": true,
	}

	// 	记录需要注意的系统调用
	Syscalls = map[string]FileAction{
		"open":      FA_Open,
		"openat":    FA_Open,
		"close":     FA_Close,
		"rename":    FA_Rename,
		"renameat":  FA_Rename,
		"renameat2": FA_Rename,
		"link":      FA_Rename,
		"unlink":    FA_Rename,
		"linkat":    FA_Rename,
		"unlinkat":  FA_Rename,
	}

	SCP  = "/usr/bin/scp"
	SFTP = "/usr/libexec/openssh/sftp-server"
)

// FileTransformExec 传输文件的程序类型
type FileTransformExec uint8

const (
	FTE_UNKNOWN FileTransformExec = iota
	FTE_SFTP                      // sftp
	FTE_SCP                       // scp
)

type FileAction uint8

const (
	FA_Open FileAction = iota
	FA_Close
	FA_Rename
	FA_Delete
)

type TaggedEvent struct {
	Event   *aucoalesce.Event
	Exec    FileTransformExec
	IsTabby bool // 标记是否为tabby客户端
	Fd      *int // 记录文件描述符
}

func FromEvent(event *aucoalesce.Event) *TaggedEvent {
	if event == nil {
		return nil
	}
	result := TaggedEvent{
		Event: event,
	}
	switch event.Process.Exe {
	case SCP:
		result.Exec = FTE_SCP
	case SFTP:
		result.Exec = FTE_SFTP
	default:
		result.Exec = FTE_UNKNOWN
	}
	// 如果是sftp，检查是否为
	if result.Exec == FTE_SFTP {

	}
	// 如果是open、openat、close，会有文件描述符

	return &result
}

// EventSet 事件集，一个事件集记录了同一个进程的审计事件
type EventSet struct {
	Pid, PPID int                           // pid和ppid
	Events    []*aucoalesce.Event           // 记录所有事件
	Fds       map[int64][]*aucoalesce.Event // 记录可以获取文件描述符的事件
	IsTabby   *bool                         // 记录是否为tabby客户端
	Exec      FileTransformExec             // 记录文件传输工具
	Lock      sync.RWMutex                  // 锁，用于保护Fds和Events的读写
}

func NewEventSet() *EventSet {
	return &EventSet{
		Events:  make([]*aucoalesce.Event, 0, 16),
		Fds:     make(map[int64][]*aucoalesce.Event),
		IsTabby: nil,
		Exec:    FTE_UNKNOWN,
		Lock:    sync.RWMutex{},
	}
}

// func (es *EventSet) AddEvent(e *aucoalesce.Event) error {
// 	if e == nil {
// 		return nil
// 	}
// 	es.Events = append(es.Events, e)
// 	sc := e.Data["syscall"]
// 	switch sc {
// 	case "close":

// 	case "openat":

// 	case "":
// 	}

// 	if sc == "openat" {
// 		i, err := ParseInt(e.Data["exit"])
// 		if err != nil {
// 			return nil
// 		}
// 	}

// 	return nil
// }

// GenKey 为事件生成字符串id，格式为 pid-ppid
func GenKey(event *aucoalesce.Event) string {
	return fmt.Sprintf("%s-%s", event.Process.PID, event.Process.PPID)
}

// FiltMsg 过滤出有用的事件，存放到map中，并触发处理程序
func FiltMsg() {
	for i := range EventChan {
		v, have := ExecPath[i.Process.Exe]
		if !(v && have) {
			// 不是指定的exe跳过
			continue
		}
		if i.Category != aucoalesce.EventTypeAuditRule {
			// 不是由审计规则触发的事件跳过
			continue
		}
		if i.Type != auparse.AUDIT_SYSCALL {
			// 不是系统调用，跳过
			continue
		}
		// if i.Data == nil {
		// 	// Data里没有数据的跳过
		// 	continue
		// }
		sc, have := i.Data["syscall"]
		if !have {
			continue
		}
		// 检查是不是需要关注的系统调用
		_, have = Syscalls[sc]
		if !have {
			continue
		}
		// 失败
		if i.Result != "success" {
			continue
		}
		printEvent(i)
	}

}

// HandleEvent 处理事件
func HaneldEvent(events []*TaggedEvent) {
	// todo
	panic("unimplemented")
}

// needHandle 判断是否需要进入处理流程
func needHandle(events []*TaggedEvent) bool {
	// todo
	panic("unimplemented")
}

func printEvent(e *aucoalesce.Event) {
	// pid，ppid，syscall，
	// open/openat：file，fd
	// close：fd
	if e == nil {
		return
	}
	fmt.Println("-----")
	switch e.Data["syscall"] {
	case "open", "openat":
		fmt.Printf("open(at) pid: %s, ppid: %s, open %s, fd: %s \n", e.Process.PID, e.Process.PPID, e.File.Path, e.Data["exit"])
	case "close":
		
		fmt.Printf("close pid: %s, ppid: %s, fd: %s \n", e.Process.PID, e.Process.PPID, e.Data["a0"])
	case "rename", "renameat", "renameat2":
		fmt.Println(json.MarshalIndent(e, "", "    "))

	case "link", "linkat":
		fmt.Printf("link(at): paths: %+v\n", e.Paths)
	case "unlink", "unlinkat":
		fmt.Printf("unlink(at): paths: %+v\n", e.Paths)

	case "dup2", "dup3":
		fmt.Println("dup")
	default:
		return
	}
}

func ParseInt(str string) (int64, error) {
	if strings.ContainsAny(str, "abcdefABCDEFxX") {
		// 是16进制数
		s := strings.ToLower(str)
		if strings.HasPrefix(s, "0x") {
			return strconv.ParseInt(s, 0, 64)
		} else {
			return strconv.ParseInt(fmt.Sprintf("0x%s", s), 0, 64)
		}
	} else {
		// 不是16进制数
		n, err := strconv.Atoi(str)
		if err != nil {
			return 0, err
		}
		return int64(n), err
	}
}
