package dbdata

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"reflect"
	"time"

	"github.com/bjdgyc/anylink/base"
	"layeh.com/radius"
	"layeh.com/radius/rfc2865"
)

type AuthRadius struct {
	Addr   string `json:"addr"`
	Secret string `json:"secret"`
	Nasip  string `json:"nasip"`
}

func init() {
	authRegistry["radius"] = reflect.TypeOf(AuthRadius{})
}

func (auth AuthRadius) checkData(authData map[string]interface{}) error {
	authType := authData["type"].(string)
	bodyBytes, err := json.Marshal(authData[authType])
	if err != nil {
		return errors.New("Radius的密钥/服务器地址填写有误")
	}
	json.Unmarshal(bodyBytes, &auth)
	if !ValidateIpPort(auth.Addr) {
		return errors.New("Radius的服务器地址填写有误")
	}
	// freeradius官网最大8000字符, 这里限制200
	if len(auth.Secret) < 8 || len(auth.Secret) > 200 {
		return errors.New("Radius的密钥长度需在8～200个字符之间")
	}
	return nil
}

func (auth AuthRadius) checkUser(name, pwd string, g *Group, ext map[string]interface{}) error {
	pl := len(pwd)
	if name == "" || pl < 1 {
		return fmt.Errorf("%s %s", name, "密码错误")
	}
	authType := g.Auth["type"].(string)
	if _, ok := g.Auth[authType]; !ok {
		return fmt.Errorf("%s %s", name, "Radius的radius值不存在")
	}
	bodyBytes, err := json.Marshal(g.Auth[authType])
	if err != nil {
		return fmt.Errorf("%s %s", name, "Radius Marshal出现错误")
	}
	err = json.Unmarshal(bodyBytes, &auth)
	if err != nil {
		return fmt.Errorf("%s %s", name, "Radius Unmarshal出现错误")
	}
	// radius认证时，设置超时3秒
	packet := radius.New(radius.CodeAccessRequest, []byte(auth.Secret))
	err = rfc2865.UserName_SetString(packet, name)
	if err != nil {
		return fmt.Errorf("%s %s", name, "Radius set name 出现错误")
	}
	err = rfc2865.UserPassword_SetString(packet, pwd)
	if err != nil {
		return fmt.Errorf("%s %s", name, "Radius set pwd 出现错误")
	}
	if auth.Nasip != "" {
		nasip := net.ParseIP(auth.Nasip)
		err = rfc2865.NASIPAddress_Set(packet, nasip)
		if err != nil {
			return fmt.Errorf("%s %s", name, "Radius set nasip 出现错误")
		}
	}
	macAddr := ""
	if ext["mac_addr"] != nil {
		macAddr = ext["mac_addr"].(string)
		base.Trace("AuthRadius", ext, macAddr)
	}
	if macAddr != "" {
		err = rfc2865.CallingStationID_AddString(packet, macAddr)
		if err != nil {
			return fmt.Errorf("%s %s", name, "Radius set CallingStationID 出现错误")
		}
	}

	ctx, done := context.WithTimeout(context.Background(), 3*time.Second)
	defer done()
	response, err := radius.Exchange(ctx, packet, auth.Addr)
	if err != nil {
		return fmt.Errorf("%s %s %s", name, "Radius服务器连接异常, 请检测服务器和端口", err)
	}
	if response.Code != radius.CodeAccessAccept {
		return fmt.Errorf("%s %s", name, "Radius：用户名或密码错误")
	}
	return nil
}
