package ldap_lib

import (
	"errors"
	"fmt"
	"strings"

	"github.com/go-ldap/ldap/v3"
	"gorm.io/gorm"
)

type Ldap struct {
	baseDN        string
	rootDN        string
	conn          *ldap.Conn
	peopleGroupDN string
	jwtSecret     string
	MysqlConn     *gorm.DB
}

func NewLdap(conn *ldap.Conn, baseDN, rootDN, peopleGroupDN, jwtSecret string, mysqlConn *gorm.DB) *Ldap {
	return &Ldap{
		baseDN:        baseDN,
		rootDN:        rootDN,
		conn:          conn,
		peopleGroupDN: peopleGroupDN,
		jwtSecret:     jwtSecret,
		MysqlConn:     mysqlConn,
	}
}

// 更新资源(分组/用户)
func (l *Ldap) Update(DN string, attr map[string][]string) error {
	if DN == l.rootDN {
		return errors.New("根用户不能更新")
	}
	modify := ldap.NewModifyRequest(DN, nil)
	for key, v := range attr {
		modify.Replace(key, v)
	}
	return l.conn.Modify(modify)
}

// 创建资源(分组/用户)
func (l *Ldap) Create(DN string, attr map[string][]string) error {
	add := ldap.NewAddRequest(DN, nil)
	for key, v := range attr {
		add.Attribute(key, v)
	}
	return l.conn.Add(add)
}

// 删除资源(分组/用户)
func (l *Ldap) Delete(DN string) error {
	if DN == l.rootDN {
		return errors.New("根用户不能删除")
	}
	del := ldap.NewDelRequest(DN, nil)
	return l.conn.Del(del)
}

// 查询资源(分组/用户)
func (l *Ldap) Search(DN string, scope int, filter string, attr []string, controls []ldap.Control) (resp *ldap.SearchResult, err error) {
	if DN == l.rootDN {
		return nil, errors.New("没有权限查询根用户")
	}
	if filter == "" {
		rootCn := strings.Split(l.rootDN, ",")
		if len(rootCn) == 0 {
			return nil, errors.New("root用户DN未设置")
		}
		filter = "(&(objectClass=*)(!(" + rootCn[0] + ")))"
	}
	searchRequest := ldap.NewSearchRequest(
		DN,
		scope, ldap.NeverDerefAliases, 0, 0, false,
		filter,
		attr,
		controls,
	)
	// 执行搜索请求
	return l.conn.Search(searchRequest)
}

// 分页查询资源(分组/用户)
func (l *Ldap) SearchWithPaging(DN string, scope int, filter string, attr []string, pageSize uint32, pagingCookie string) (resp *ldap.SearchResult, err error) {
	if DN == l.rootDN {
		return nil, errors.New("没有权限查询根用户")
	}
	if filter == "" {
		rootCn := strings.Split(l.rootDN, ",")
		if len(rootCn) == 0 {
			return nil, errors.New("root用户DN未设置")
		}
		filter = "(&(objectClass=*)(!(" + rootCn[0] + ")))"
	}
	searchRequest := ldap.NewSearchRequest(
		DN,
		scope, ldap.NeverDerefAliases, 0, 0, false,
		filter,
		attr,
		nil,
	)
	pagingCtl := ldap.NewControlPaging(pageSize)
	pagingCtl.SetCookie([]byte(pagingCookie))
	searchRequest.Controls = []ldap.Control{
		pagingCtl,
	}
	// 执行搜索请求
	return l.conn.Search(searchRequest)
}

// *********************************************************************************************
// 查询组织列表
type OrganizationInfo struct {
	Name    string   `json:"name"`
	DN      string   `json:"dn"`
	Owner   string   `json:"owner"`
	Members []string `json:"members"`
}

func (l *Ldap) GetOrganizationList(scope int, organizationDNs []string, fields []string, control []ldap.Control) ([]OrganizationInfo, error) {
	filterBuilder := strings.Builder{}
	for _, dn := range organizationDNs {
		//提取 ou
		filterBuilder.WriteString(fmt.Sprintf("(%s)", strings.Split(dn, ",")[0]))
	}
	filter := "(&(objectClass=groupOfUniqueNames)(objectClass=top))"
	if filterBuilder.Len() > 0 {
		filter = "(&(objectClass=groupOfUniqueNames)(objectClass=top)(|" + filterBuilder.String() + "))"
	}
	organizationsResult, err := l.Search(l.baseDN, scope, filter, fields, nil)
	if err != nil {
		return nil, err
	}

	list := make([]OrganizationInfo, 0, len(organizationsResult.Entries))
	for _, entry := range organizationsResult.Entries {
		data := OrganizationInfo{
			DN: entry.DN,
		}
		for _, attr := range entry.Attributes {
			switch attr.Name {
			case "businessCategory": //名称
				data.Name = strings.Join(attr.Values, ",")
			case "owner": //负责人
				data.Owner = strings.Join(attr.Values, ",")
			case "uniqueMember": //成员
				data.Members = attr.Values
			}
		}
		list = append(list, data)
	}
	return list, nil
}

// AddUserToGroup 添加用户到组织
func (l *Ldap) AddUserToOrganization(organizationDN, userDN string) error {
	modify := ldap.NewModifyRequest(organizationDN, nil)
	modify.Add("uniqueMember", []string{userDN})
	return l.conn.Modify(modify)
}

// DelUserFromGroup 将用户从分组删除
func (l *Ldap) RemoveUserFromOrganization(groupDN, userDN string) error {
	if userDN == l.rootDN {
		return errors.New("根用户不能从分组删除")
	}
	modify := ldap.NewModifyRequest(groupDN, nil)
	modify.Delete("uniqueMember", []string{userDN})
	return l.conn.Modify(modify)
}