package snmp

import (
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"
	"time"

	g "github.com/gosnmp/gosnmp"
	"github.com/slayercat/GoSNMPServer"
	"github.com/slayercat/GoSNMPServer/mibImps"
)

type SNMPService struct {
	ListenAddr string
	ListenPort uint16
	UserName   string
	AuthPass   string
	AuthProto  string
	PrivPass   string
	PrivProto  string
	EngineID   string
	TrapPort   uint16
	TrapListen bool
	TrapBool   bool
	TrapTick   uint16
	TimeOut    uint16
	TrapTarget string

	ListenHost string
	TrapHost   string

	SysName     string
	SysDescr    string
	SysLocation string
	SysContact  string
	SysStatus   string
	SysService  int
}

func (s *SNMPService) getAuthProto() g.SnmpV3AuthProtocol {
	switch s.AuthProto {
	case "NoAuth":
		return g.NoAuth
	case "MD5":
		return g.MD5
	case "SHA":
		return g.SHA
	default:
	}
	return g.MD5
}

func (s *SNMPService) getPrivProto() g.SnmpV3PrivProtocol {
	switch s.PrivProto {
	case "NoPriv":
		return g.NoPriv
	case "DES":
		return g.DES
	case "AES":
		return g.AES
	case "AES192":
		return g.AES192
	case "AES256":
		return g.AES256
	default:
	}
	return g.DES
}

func (s *SNMPService) setSecParamsList() []g.UsmSecurityParameters {
	var secParamsList = []g.UsmSecurityParameters{
		{
			UserName:                 s.UserName,
			AuthenticationProtocol:   s.getAuthProto(),
			AuthenticationPassphrase: s.AuthPass,
			PrivacyProtocol:          s.getPrivProto(),
			PrivacyPassphrase:        s.PrivPass,
			AuthoritativeEngineID:    s.EngineID,
		},
		// {
		// 	UserName:                 "myuser2",
		// 	AuthenticationProtocol:   g.SHA,
		// 	AuthenticationPassphrase: "mypassword2",
		// 	PrivacyProtocol:          g.DES,
		// 	PrivacyPassphrase:        "myprivacy2",
		// 	AuthoritativeEngineID:    s.EngineID,
		// },
		// {
		// 	UserName:                 "myuser2",
		// 	AuthenticationProtocol:   g.MD5,
		// 	AuthenticationPassphrase: "mypassword2",
		// 	PrivacyProtocol:          g.AES,
		// 	PrivacyPassphrase:        "myprivacy2",
		// 	AuthoritativeEngineID:    s.EngineID,
		// },
	}
	return secParamsList
}

func (s *SNMPService) StartSNMPServer() {
	// 设置引擎启动次数和引varvar
	var engineBoots uint32 = 1
	//var engineTime uint32 = uint32(time.Now().Unix() % 2147483647) // 使用当前时间初始化
	//var engineTime uint32 = 3600 // 使用当前时间初始化
	master := GoSNMPServer.MasterAgent{
		Logger: GoSNMPServer.NewDefaultLogger(),
		SecurityConfig: GoSNMPServer.SecurityConfig{
			NoSecurity:               true,
			AuthoritativeEngineBoots: engineBoots,
			// OnGetAuthoritativeEngineTime: func() uint32 {
			// 	return engineTime
			// },
			//AuthoritativeEngineID:    GoSNMPServer.SNMPEngineID{EngineIDData: "0x800007DB03360102101100"},

			Users: s.setSecParamsList(),
		},
		SubAgents: []*GoSNMPServer.SubAgent{
			{
				UserErrorMarkPacket: false,
				CommunityIDs:        []string{"public", "private"}, // SNMPv1 and SNMPv2c community strings
				OIDs:                s.handleOIDs(),
				//OIDs:                mibImps.All(),
			},
		},
	}

	server := GoSNMPServer.NewSNMPServer(master)
	err := server.ListenUDP("udp", s.ListenHost)
	if err != nil {
		log.Fatalf("Error in listen: %+v", err)
	}
	server.ServeForever()
}

func (s *SNMPService) handleOIDs() []*GoSNMPServer.PDUValueControlItem {
	customOIDs := []*GoSNMPServer.PDUValueControlItem{
		{
			OID:  "1.3.6.1.4.1.1379.2.3.3.3.1.1.1.0",
			Type: g.OctetString,
			OnGet: func() (value interface{}, err error) {
				return s.SysName, nil
			},
			OnSet: func(value interface{}) error {
				// 将[]uint8转换为string
				if v, ok := value.([]uint8); ok {
					s.SysName = string(v)
					log.Printf("Set request for OID 1.3.6.1.4.1.1379.2.3.3.3.1.1.1.0 with value %v", s.SysName)
					return nil
				}
				return nil
			},
		},
		{
			OID:  ".1.3.6.1.4.1.1379.2.3.3.3.1.1.2.0",
			Type: g.OctetString,
			OnGet: func() (value interface{}, err error) {
				return s.SysStatus, nil
			},
			OnSet: func(value interface{}) error {
				// 将[]uint8转换为string
				if v, ok := value.([]uint8); ok {
					s.SysStatus = string(v)
					log.Printf("Set request for OID 1.3.6.1.4.1.1379.2.3.3.3.1.1.2.0 with value %v", s.SysStatus)
					return nil
				}
				return nil
			},
		},
		{
			OID:  ".1.3.6.1.4.1.1379.2.3.3.3.1.1.3.0",
			Type: g.OctetString,
			OnGet: func() (value interface{}, err error) {
				return s.SysDescr, nil
			},
			OnSet: func(value interface{}) error {
				// 将[]uint8转换为string
				if v, ok := value.([]uint8); ok {
					s.SysDescr = string(v)
					log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.3.0 with value %v", s.SysDescr)
					return nil
				}
				return nil
			},
		},
		{
			OID:  ".1.3.6.1.4.1.1379.2.3.3.3.1.1.4.0",
			Type: g.OctetString,
			OnGet: func() (value interface{}, err error) {
				return s.SysLocation, nil
			},
			OnSet: func(value interface{}) error {
				// 将[]uint8转换为string
				if v, ok := value.([]uint8); ok {
					s.SysLocation = string(v)
					log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.4.0 with value %v", s.SysLocation)
					return nil
				}
				return nil
			},
		},
		{
			OID:  ".1.3.6.1.4.1.1379.2.3.3.3.1.1.5.0",
			Type: g.OctetString,
			OnGet: func() (value interface{}, err error) {
				return s.SysContact, nil
			},
			OnSet: func(value interface{}) error {
				// 将[]uint8转换为string
				if v, ok := value.([]uint8); ok {
					s.SysContact = string(v)
					log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.5.0 with value %v", s.SysContact)
					return nil
				}
				return nil
			},
		},
		{
			OID:  ".1.3.6.1.4.1.1379.2.3.3.3.1.1.7.0",
			Type: g.TimeTicks,
			OnGet: func() (value interface{}, err error) {
				return uint32(time.Now().Unix()), nil
			},
		},
		{
			OID:  ".1.3.6.1.4.1.1379.2.3.3.3.1.1.9.0",
			Type: g.Integer,
			OnGet: func() (value interface{}, err error) {
				return s.SysService, nil
			},
			OnSet: func(value interface{}) error {
				if v, ok := value.(int); ok {
					s.SysService = v
					log.Printf("Set request for OID .1.3.6.1.4.1.1379.2.3.3.3.1.1.9.0 with value %v", s.SysService)
					return nil
				}
				return nil
			},
		},
	}
	// 为 GETBULK 新增处理 OIDs
	bulkOIDs := &GoSNMPServer.PDUValueControlItem{
		OID:  ".1.3.6.1.4.1.1379.2.3.3.3.1.1", // 这里是您想要支持 GETBULK 的 OID 前缀
		Type: g.OctetString,
		OnGet: func() (value interface{}, err error) {
			// 假设我们返回一百度值，您可以根据您的实现进行调整
			values := []interface{}{s.SysName, s.SysStatus, s.SysDescr, s.SysLocation, s.SysContact, uint32(time.Now().Unix()), s.SysService} // 可以从其他结构中获取真实值
			return values, nil
		},
	}

	customOIDs = append(customOIDs, bulkOIDs)

	// 获取mibImps.All()返回的OID列表
	mibOIDs := mibImps.All()

	// 使用Map来检测并移除重复的OID
	oidMap := make(map[string]*GoSNMPServer.PDUValueControlItem)
	for _, oid := range customOIDs {
		oidMap[oid.OID] = oid
	}
	for _, oid := range mibOIDs {
		if _, exists := oidMap[oid.OID]; !exists {
			oidMap[oid.OID] = oid
		} else {
			log.Printf("Duplicate OID found: %s", oid.OID)
		}
	}

	// 将Map转换为Slice
	allOIDs := make([]*GoSNMPServer.PDUValueControlItem, 0, len(oidMap))
	for _, oid := range oidMap {
		allOIDs = append(allOIDs, oid)
	}

	return allOIDs
}

func (s *SNMPService) StartTrapServer() {
	flag.Usage = func() {
		fmt.Printf("Usage:\n")
		fmt.Printf("   %s\n", filepath.Base(os.Args[0]))
		flag.PrintDefaults()
	}

	tl := g.NewTrapListener()
	tl.OnNewTrap = s.MyTrapHandler

	usmTable := g.NewSnmpV3SecurityParametersTable(g.NewLogger(log.New(os.Stdout, "", 0)))
	for i := range s.setSecParamsList() {
		sp := &s.setSecParamsList()[i] // 使用指针
		err := usmTable.Add(sp.UserName, sp)
		if err != nil {
			usmTable.Logger.Print(err)
		}
	}

	// 设置引擎启动次数和引varvar
	//var engineBoots uint32 = 1
	// var engineTime uint32 = uint32(time.Now().Unix() % 2147483647) // 使用当前时间初始化
	//var engineTime uint32 = 3600 // 使用当前时间初始化
	gs := &g.GoSNMP{
		Target:        s.TrapTarget,
		Port:          s.TrapPort,
		Transport:     "udp",
		Timeout:       time.Duration(s.TimeOut) * time.Second, // 设置超时时间为x秒
		Version:       g.Version3,                             // Always using version3 for traps, only option that works with all SNMP versions simultaneously
		MsgFlags:      g.NoAuthNoPriv,
		SecurityModel: g.UserSecurityModel,
		SecurityParameters: &g.UsmSecurityParameters{
			UserName:                 s.UserName,
			AuthoritativeEngineID:    s.EngineID,
			AuthoritativeEngineBoots: 1,
			//AuthoritativeEngineTime:  3600,
			AuthenticationProtocol:   s.getAuthProto(),
			AuthenticationPassphrase: s.AuthPass,
			PrivacyProtocol:          s.getPrivProto(),
			PrivacyPassphrase:        s.PrivPass,
		},
		//TrapSecurityParametersTable: usmTable,
		ContextEngineID: s.EngineID,
		ContextName:     "v3test",
	}
	tl.Params = gs
	tl.Params.Logger = g.NewLogger(log.New(os.Stdout, "", 0))

	// 定时发送Trap
	if s.TrapBool {
		go s.SendPeriodicTraps(gs)
	}
	go s.monitorNetwork(gs)

	if s.TrapListen {
		err := tl.Listen(s.TrapHost)
		if err != nil {
			log.Panicf("error in listen: %s", err)
		}
	}
}

func (s *SNMPService) MyTrapHandler(packet *g.SnmpPacket, addr *net.UDPAddr) {
	log.Printf("got trapdata from %s\n", addr.IP)
	for _, v := range packet.Variables {
		switch v.Type {
		case g.OctetString:
			b := v.Value.([]byte)
			fmt.Printf("OID: %s, string: %x\n", v.Name, b)

		default:
			log.Printf("trap: %+v\n", v)
		}
	}
}

func (s *SNMPService) SendPeriodicTraps(gs *g.GoSNMP) {
	err := gs.Connect()
	if err != nil {
		log.Fatalf("Connect() err: %v", err)
	}
	defer gs.Conn.Close()

	ticker := time.NewTicker(time.Duration(s.TrapTick) * time.Second) // 每10秒发送一次Trap
	defer ticker.Stop()

	for range ticker.C { // 每x秒发送一次Trap
		trap := g.SnmpTrap{
			Variables: []g.SnmpPDU{
				{
					Name:  ".1.3.6.1.2.1.1.3.0",
					Type:  g.TimeTicks,
					Value: uint32(time.Now().Unix()),
				},
				{
					Name:  ".1.3.6.1.6.3.1.1.4.1.0",
					Type:  g.ObjectIdentifier,
					Value: ".1.3.6.1.6.3.1.1.5.1",
				},
			},
		}
		_, err = gs.SendTrap(trap)
		if err != nil {
			log.Printf("error sending trap: %s", err)
		} else {
			log.Printf("trap sent successfully")
		}
	}
}

// 1. 设备链路连接失败时发送Trap (LinkDown)
func (s *SNMPService) sendLinkDownTrap(gs *g.GoSNMP, ifIndex int, ifDescr string) {
	err := gs.Connect()
	if err != nil {
		log.Fatalf("Connect() err: %v", err)
	}
	defer gs.Conn.Close()

	trap := g.SnmpTrap{
		Variables: []g.SnmpPDU{
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.1", // linkDown
				Type:  g.OctetString,
				Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.1",
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.1.1", // ifIndex
				Type:  g.Integer,
				Value: ifIndex,
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.1.2", // ifDescr
				Type:  g.OctetString,
				Value: ifDescr,
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
				Type:  g.Integer,
				Value: 2, // event
			},
		},
	}

	_, err = gs.SendTrap(trap)
	if err != nil {
		log.Printf("error sending LinkDown trap: %s", err)
	} else {
		log.Printf("LinkDown trap sent successfully")
	}
}

// 2. 设备链路恢复正常时发送Trap (LinkUp)
func (s *SNMPService) sendLinkUpTrap(gs *g.GoSNMP, ifIndex int, ifDescr string) {
	err := gs.Connect()
	if err != nil {
		log.Fatalf("Connect() err: %v", err)
	}
	defer gs.Conn.Close()

	trap := g.SnmpTrap{
		Variables: []g.SnmpPDU{
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.2", // linkUp
				Type:  g.OctetString,
				Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.2",
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.2.1", // ifIndex
				Type:  g.Integer,
				Value: ifIndex,
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.2.2", // ifDescr
				Type:  g.OctetString,
				Value: ifDescr,
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
				Type:  g.Integer,
				Value: 5, // event
			},
		},
	}

	_, err = gs.SendTrap(trap)
	if err != nil {
		log.Printf("error sending LinkUp trap: %s", err)
	} else {
		log.Printf("LinkUp trap sent successfully")
	}
}

// 3. 设备鉴权失败时发送Trap (AuthenticationFailure)
func (s *SNMPService) sendAuthFailureTrap(gs *g.GoSNMP, username, descr string) {
	err := gs.Connect()
	if err != nil {
		log.Fatalf("Connect() err: %v", err)
	}
	defer gs.Conn.Close()

	trap := g.SnmpTrap{
		Variables: []g.SnmpPDU{
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.3", // authenticationFailure
				Type:  g.OctetString,
				Value: ".1.3.6.1.4.1.1379.2.3.3.3.3.3",
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.3.1", // 自定义OID，用于记录失败的用户名
				Type:  g.OctetString,
				Value: username,
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.3.3.2", // 自定义OID，用于记录描述
				Type:  g.OctetString,
				Value: descr,
			},
			{
				Name:  ".1.3.6.1.4.1.1379.2.3.3.3.4", // severity OID
				Type:  g.Integer,
				Value: 4, // event
			},
		},
	}

	_, err = gs.SendTrap(trap)
	if err != nil {
		log.Printf("error sending AuthenticationFailure trap: %s", err)
	} else {
		log.Printf("AuthenticationFailure trap sent successfully")
	}
}

func (s *SNMPService) monitorNetwork(gs *g.GoSNMP) {
	// 假设有一个函数 checkLinkStatus 返回链路状态
	for {
		serviceStatus := s.checkServiceStatus()
		switch strings.ToUpper(serviceStatus) {
		case "LINK_DOWN":
			index := 1
			ifDescr := fmt.Sprintf("Link(index=%d) DOWN", index)
			s.sendLinkDownTrap(gs, index, ifDescr) // 假设接口索引为1
			s.SysService = 0
		case "LINK_UP":
			index := 1
			ifDescr := fmt.Sprintf("Link(index=%d) UP", index)
			s.sendLinkUpTrap(gs, index, ifDescr) // 假设接口索引为1
			s.SysService = 0
		case "AUTH_FAILURE":
			descr := "Authentication Failure"
			s.sendAuthFailureTrap(gs, s.UserName, descr)
			s.SysService = 0
		default:
		}

		time.Sleep(10 * time.Second) // 每10秒检查一次
	}
}

func (s *SNMPService) checkServiceStatus() string {
	switch s.SysService {
	case 1:
		return "LINK_DOWN"
	case 2:
		return "LINK_UP"
	case 3:
		return "AUTH_FAILURE"
	default:
	}
	return "NORMAL"
}
