package backend

import (
	"context"
	"dhcp/api/request"
	"dhcp/code"
	"dhcp/internal/dhcpServer/handler/proxy"
	"dhcp/internal/dhcpServer/server"
	"dhcp/internal/ip"
	"dhcp/result"
	"fmt"
	"github.com/go-logr/logr"
	"github.com/insomniacslk/dhcp/dhcpv4/server4"
	"net"
	"net/netip"
)

var DHCPServiceMap = map[string]*DHCPService{}
var DHCPServiceInfoMap = map[string]request.DHCPService{}

type DhcpConfig struct {
	Mode              string `yaml:"model"`                // 模式
	BindAddr          string `yaml:"bind_addr"`            // 绑定地址
	BindInterface     string `yaml:"bind_interface"`       //	绑定接口
	IpForPacket       string `yaml:"ip_for_packet"`        // 包的ip
	SyslogIP          string `yaml:"syslog_ip"`            // syslog ip
	TftpIP            string `yaml:"tftp_ip"`              // tftp ip
	HttpIpxeBinaryURL string `yaml:"http_ipxe_binary_url"` // http ipxe二进制url
}

func StartDHCPService(ctx context.Context, log logr.Logger, req request.DHCPService) result.Result {
	dhcp := DhcpConfig{
		Mode:          req.Mode,
		BindAddr:      "0.0.0.0:67",
		BindInterface: req.BindInterface,
		IpForPacket:   ip.DetectPublicIPv4(""),
		SyslogIP:      ip.DetectPublicIPv4(""),
		TftpIP:        ip.DetectPublicIPv4(":69"),
	}
	var dhcpService = &DHCPService{}
	dhcpService.StartDHCP(ctx, dhcp, log)
	DHCPServiceMap[req.BindInterface] = dhcpService
	req.Status = true
	DHCPServiceInfoMap[req.BindInterface] = req
	return result.Result{
		Status: true,
		Code:   code.SUCCESS,
		Msg:    "启动成功",
	}
}

func StopDHCPService(ctx context.Context, log logr.Logger, req request.DHCPService) result.Result {
	if dhcpService, ok := DHCPServiceMap[req.BindInterface]; ok {
		err := dhcpService.server.Close()
		if err != nil {
			return result.Result{
				Status: false,
				Code:   code.SystemException,
				Msg:    "关闭失败",
			}
		}
		delete(DHCPServiceMap, req.BindInterface)
		DHCPServiceInfo := DHCPServiceInfoMap[req.BindInterface]
		DHCPServiceInfo.Status = false
		DHCPServiceInfo.Info = "自主关闭"
		DHCPServiceInfoMap[req.BindInterface] = DHCPServiceInfo
	}
	return result.Result{
		Status: true,
		Code:   code.SUCCESS,
		Msg:    "关闭成功",
	}
}

func DeleteDHCPService(ctx context.Context, log logr.Logger, req request.DHCPService) result.Result {
	if dhcpService, ok := DHCPServiceMap[req.BindInterface]; ok {
		_ = dhcpService.server.Close()
		delete(DHCPServiceMap, req.BindInterface)
		delete(DHCPServiceInfoMap, req.BindInterface)
	} else {
		return result.Result{
			Status: false,
			Code:   code.SystemException,
			Msg:    "删除失败",
		}
	}
	return result.Result{
		Status: true,
		Code:   code.SUCCESS,
		Msg:    "删除成功",
	}
}

type DHCPService struct {
	server *server.DHCP
}

func (dhcp *DHCPService) StartDHCP(ctx context.Context, d DhcpConfig, log logr.Logger) {
	dh, err := dhcpHandler(ctx, &d, log)
	if err != nil {
		log.Error(err, "failed to create dhcpServer listener")
		panic(fmt.Errorf("failed to create dhcpServer listener: %w", err))
	}
	log.Info("starting dhcpServer server", "bind_addr", d.BindAddr)
	go func(ctx context.Context) {
		bindAddr, err := netip.ParseAddrPort(d.BindAddr)
		if err != nil {
			panic(fmt.Errorf("invalid tftp address for dhcpServer server: %w", err))
		}
		conn, err := server4.NewIPv4UDPConn(d.BindInterface, net.UDPAddrFromAddrPort(bindAddr))
		if err != nil {
			log.Info("failed to create dhcpServer listener", "error", err)
			panic(err)
		}
		defer conn.Close()
		dhcp.server = &server.DHCP{Logger: log, Conn: conn, Handlers: []server.Handler{dh}}
		err = dhcp.server.Serve(ctx)

		if err != nil {
			delete(DHCPServiceMap, d.BindInterface)
			DHCPServiceInfo := DHCPServiceInfoMap[d.BindInterface]
			DHCPServiceInfo.Status = false
			DHCPServiceInfo.Info = err.Error()
			DHCPServiceInfoMap[d.BindInterface] = DHCPServiceInfo
		}
	}(ctx)
}

func (dhcp *DHCPService) StopDHCP(ctx context.Context) {
	dhcp.server.Close()
}

func dhcpHandler(ctx context.Context, c *DhcpConfig, log logr.Logger) (server.Handler, error) {
	// 1. create the handler
	// 2. create the macAndIPXE
	// 3. add the macAndIPXE to the handler
	pktIP, err := netip.ParseAddr(c.IpForPacket)
	if err != nil {
		return nil, fmt.Errorf("invalid bind address: %w", err)
	}
	br := NewBackend()
	dh := &proxy.Handler{
		Backend: br,
		IPAddr:  pktIP,
		Log:     log,
	}
	return dh, nil
}
