package ldap

import (
	"crypto/tls"
	"fmt"
	"strings"
	"time"

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

// Client 封装 LDAP/AD 域连接
type Client struct {
	conn *ldap.Conn
}

// ClientConfig LDAP 连接参数
type ClientConfig struct {
	Host       string
	Port       int
	BindDN     string
	Password   string
	BaseDN     string
	UseTLS     bool
	SkipVerify bool
	Timeout    time.Duration
}

// NewClient 创建并绑定 LDAP 连接
func NewClient(cfg ClientConfig) (*Client, error) {
	if cfg.Port == 0 {
		if cfg.UseTLS {
			cfg.Port = 636
		} else {
			cfg.Port = 389
		}
	}
	if cfg.Timeout == 0 {
		cfg.Timeout = 15 * time.Second
	}

	var conn *ldap.Conn
	var err error

	addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)

	if cfg.UseTLS {
		tlsConfig := &tls.Config{
			InsecureSkipVerify: cfg.SkipVerify,
			ServerName:         cfg.Host,
		}
		conn, err = ldap.DialTLS("tcp", addr, tlsConfig)
	} else {
		conn, err = ldap.Dial("tcp", addr)
	}

	if err != nil {
		return nil, fmt.Errorf("连接 LDAP %s 失败: %w", addr, err)
	}

	conn.SetTimeout(cfg.Timeout)

	// 绑定管理员账户
	if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil {
		conn.Close()
		return nil, fmt.Errorf("LDAP 绑定失败: %w", err)
	}

	return &Client{conn: conn}, nil
}

// Close 关闭连接
func (c *Client) Close() {
	if c.conn != nil {
		c.conn.Close()
	}
}

// UserAccount AD 用户账户信息
type UserAccount struct {
	DN             string
	Username       string
	DisplayName    string
	Email          string
	Enabled        bool
	Locked         bool
	PasswordExpired bool
	PasswordNeverExpires bool
	LastLogon      time.Time
	PwdLastSet     time.Time
	AccountCreated time.Time
	MemberOf       []string
	Description    string
}

// SearchUsers 搜索 AD 用户
// filterExpr 为额外的过滤条件（LDAP 语法），为空则使用默认用户过滤器
func (c *Client) SearchUsers(baseDN, filterExpr string) ([]*UserAccount, error) {
	if baseDN == "" {
		return nil, fmt.Errorf("baseDN 不能为空")
	}

	// 默认过滤所有用户对象
	filter := "(objectClass=user)(objectCategory=person)"
	if filterExpr != "" {
		filter = fmt.Sprintf("(&%s%s)", filter, filterExpr)
	} else {
		filter = fmt.Sprintf("(&%s(!(objectClass=computer)))", filter)
	}

	attributes := []string{
		"distinguishedName",
		"sAMAccountName",
		"displayName",
		"mail",
		"userAccountControl",
		"lastLogon",
		"pwdLastSet",
		"whenCreated",
		"memberOf",
		"description",
	}

	searchReq := ldap.NewSearchRequest(
		baseDN,
		ldap.ScopeWholeSubtree,
		ldap.NeverDerefAliases,
		0,     // 不限制结果数
		0,     // 不超时
		false, // 不只取类型
		filter,
		attributes,
		nil,
	)

	result, err := c.conn.Search(searchReq)
	if err != nil {
		return nil, fmt.Errorf("LDAP 搜索失败: %w", err)
	}

	users := make([]*UserAccount, 0, len(result.Entries))
	for _, entry := range result.Entries {
		user := parseUserEntry(entry)
		users = append(users, user)
	}

	return users, nil
}

// parseUserEntry 解析 LDAP 条目为 UserAccount
func parseUserEntry(entry *ldap.Entry) *UserAccount {
	user := &UserAccount{
		DN:          entry.GetAttributeValue("distinguishedName"),
		Username:    entry.GetAttributeValue("sAMAccountName"),
		DisplayName: entry.GetAttributeValue("displayName"),
		Email:       entry.GetAttributeValue("mail"),
		Description: entry.GetAttributeValue("description"),
		MemberOf:    entry.GetAttributeValues("memberOf"),
	}

	// 解析 userAccountControl 位标志
	uacStr := entry.GetAttributeValue("userAccountControl")
	uac := parseUAC(uacStr)
	user.Enabled = !uac.Disabled
	user.Locked = uac.Locked
	user.PasswordExpired = uac.PasswordExpired
	user.PasswordNeverExpires = uac.PasswordNeverExpires

	// 解析时间字段
	user.LastLogon = parseADTime(entry.GetAttributeValue("lastLogon"))
	user.PwdLastSet = parseADTime(entry.GetAttributeValue("pwdLastSet"))
	user.AccountCreated = parseLDAPTime(entry.GetAttributeValue("whenCreated"))

	return user
}

// UACFlags userAccountControl 位标志
type UACFlags struct {
	Disabled            bool
	Locked              bool
	PasswordExpired     bool
	PasswordNeverExpires bool
}

// parseUAC 解析 userAccountControl 数值
func parseUAC(uacStr string) UACFlags {
	var flags UACFlags
	if uacStr == "" {
		return flags
	}

	var uac int64
	fmt.Sscanf(uacStr, "%d", &uac)

	flags.Disabled = uac&0x0002 != 0           // ACCOUNTDISABLE
	flags.Locked = uac&0x0010 != 0             // LOCKOUT
	flags.PasswordExpired = uac&0x800000 != 0  // PASSWORD_EXPIRED
	flags.PasswordNeverExpires = uac&0x10000 != 0 // DONT_EXPIRE_PASSWORD

	return flags
}

// parseADTime 解析 AD 时间戳（100纳秒间隔，从 1601-01-01 起）
func parseADTime(timestamp string) time.Time {
	if timestamp == "" || timestamp == "0" {
		return time.Time{}
	}

	var intervals int64
	fmt.Sscanf(timestamp, "%d", &intervals)
	if intervals <= 0 {
		return time.Time{}
	}

	// AD 时间戳从 1601-01-01 UTC 起，以 100 纳秒为单位
	base := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
	return base.Add(time.Duration(intervals) * 100 * time.Nanosecond)
}

// parseLDAPTime 解析 LDAP generalized time 格式 (YYYYMMDDHHMMSS.0Z)
func parseLDAPTime(t string) time.Time {
	if t == "" {
		return time.Time{}
	}
	// 尝试解析 YYYYMMDDHHMMSS.0Z 格式
	if len(t) >= 15 {
		layout := "20060102150405Z"
		// 去掉小数部分
		if dotIdx := strings.Index(t, "."); dotIdx >= 0 {
			t = t[:dotIdx] + "Z"
		}
		parsed, err := time.Parse(layout, t)
		if err == nil {
			return parsed
		}
	}
	return time.Time{}
}

// SearchGroupMembers 查询指定组的成员
func (c *Client) SearchGroupMembers(baseDN, groupDN string) ([]*UserAccount, error) {
	filter := fmt.Sprintf("(&(objectClass=user)(objectCategory=person)(memberOf=%s))", groupDN)
	return c.SearchUsers(baseDN, filter)
}
