package teamspeak

import (
	"context"
	"errors"
	"fmt"
	"log"
	"log/slog"
	"strconv"
	"strings"
	"time"

	"github.com/honeybbq/teamspeak-go/commands"
)

var errNoDataReturnedForClient = errors.New("no data returned for client")

// SendTextMessage sends a text message to a client, channel or server.
func (c *Client) SendTextMessage(targetMode int, targetID uint64, message string) error {
	cmd := commands.BuildCommandOrdered("sendtextmessage", [][2]string{
		{"targetmode", strconv.Itoa(targetMode)},
		{"target", strconv.FormatUint(targetID, 10)},
		{"msg", message},
	})

	return c.SendCommandNoWait(cmd)
}

// ClientMove moves a client to a different channel.
func (c *Client) ClientMove(clid uint16, channelID uint64, password string) error {
	params := [][2]string{
		{"clid", strconv.Itoa(int(clid))},
		{"cid", strconv.FormatUint(channelID, 10)},
	}
	if password != "" {
		params = append(params, [2]string{"cpw", prepareClientPassword(password)})
	}
	cmd := commands.BuildCommandOrdered("clientmove", params)

	return c.ExecCommand(cmd, 10*time.Second)
}

// Poke sends a poke message to a client.
func (c *Client) Poke(clid uint16, message string) error {
	cmd := commands.BuildCommandOrdered("clientpoke", [][2]string{
		{"clid", strconv.Itoa(int(clid))},
		{"msg", message},
	})

	return c.ExecCommand(cmd, 10*time.Second)
}

// SendVoice sends a raw Opus frame. Codec values: 4 = Opus voice, 5 = Opus music.
func (c *Client) SendVoice(data []byte, codec byte) error {
	return c.handler.SendVoicePacket(data, codec)
}

// ClientID returns the client's own ID assigned by the server.
func (c *Client) ClientID() uint16 {
	c.mu.Lock()
	defer c.mu.Unlock()

	return c.clid
}

// GetClientInfo fetches detailed information about a client.
func (c *Client) GetClientInfo(clid uint16) (map[string]string, error) {
	cmd := fmt.Sprintf("clientinfo clid=%d", clid)
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return nil, err
	}
	if len(data) == 0 {
		return nil, fmt.Errorf("%w: %d", errNoDataReturnedForClient, clid)
	}

	return data[0], nil
}

// ListChannels returns a list of all channels on the server.
func (c *Client) ListChannels() ([]ChannelInfo, error) {
	data, err := c.ExecCommandWithResponse("channellist", 5*time.Second)
	if err != nil {
		log.Printf("[teamspeak-go] ListChannels ExecCommandWithResponse error: %v", err)
		return nil, err
	}
	log.Printf("[teamspeak-go] ListChannels got %d raw data rows", len(data))
	for i, item := range data {
		log.Printf("[teamspeak-go] ListChannels row[%d]: %v", i, item)
	}

	channels := make([]ChannelInfo, 0, len(data))
	for _, item := range data {
		cid, _ := parseUint64Value(item["cid"])
		pid, _ := parseUint64Value(item["pid"])
		name := item["channel_name"]

		channels = append(channels, ChannelInfo{
			ID:       cid,
			ParentID: pid,
			Name:     commands.Unescape(name),
		})
	}

	return channels, nil
}

// ListChannelsDetailed returns channels with extended properties.
// 通过 channellist -topic -flags -voice -limits -icon 获取更丰富的频道属性。
func (c *Client) ListChannelsDetailed() ([]ChannelInfoDetailed, error) {
	data, err := c.ExecCommandWithResponse(
		"channellist -topic -flags -voice -limits -icon", 5*time.Second)
	if err != nil {
		return nil, err
	}

	channels := make([]ChannelInfoDetailed, 0, len(data))
	for _, item := range data {
		cid, _ := parseUint64Value(item["cid"])
		pid, _ := parseUint64Value(item["pid"])
		order, _ := parseUint64Value(item["channel_order"])
		name := item["channel_name"]
		topic := item["channel_topic"]

		codec, _ := parseIntValue(item["channel_codec"])
		codecQuality, _ := parseIntValue(item["channel_codec_quality"])
		neededTalkPower, _ := parseIntValue(item["channel_needed_talk_power"])

		maxClients, _ := parseIntValue(item["channel_maxclients"])
		maxFamilyClients, _ := parseIntValue(item["channel_maxfamilyclients"])

		iconID, _ := parseInt64Value(item["channel_icon_id"])
		neededModifyPower, _ := parseIntValue(item["channel_needed_modify_power"])

		channels = append(channels, ChannelInfoDetailed{
			ID:                              cid,
			ParentID:                        pid,
			Order:                           order,
			Name:                            commands.Unescape(name),
			Topic:                           commands.Unescape(topic),
			Codec:                           codec,
			CodecQuality:                    codecQuality,
			NeededTalkPower:                 neededTalkPower,
			MaxClients:                      maxClients,
			MaxFamilyClients:                maxFamilyClients,
			IsMaxClientsUnlimited:           parseBoolValue(item["channel_flag_maxclients_unlimited"]),
			IsMaxFamilyClientsUnlimited:     parseBoolValue(item["channel_flag_maxfamilyclients_unlimited"]),
			IsOrdered:                       order > 0,
			IsPermanent:                     parseBoolValue(item["channel_flag_permanent"]),
			IsSemiPermanent:                 parseBoolValue(item["channel_flag_semi_permanent"]),
			IsDefault:                       parseBoolValue(item["channel_flag_default"]),
			IsPassword:                      parseBoolValue(item["channel_flag_password"]),
			HasPassword:                     parseBoolValue(item["channel_flag_password"]),
			NeededModifyPower:               neededModifyPower,
			IconID:                          iconID,
		})
	}

	return channels, nil
}

// ListClients returns a list of all clients currently connected to the server.
func (c *Client) ListClients() ([]ClientInfo, error) {
	data, err := c.ExecCommandWithResponse("clientlist -uid -away -voice -groups", 5*time.Second)
	if err != nil {
		return nil, err
	}

	clients := make([]ClientInfo, 0, len(data))
	for _, item := range data {
		clid, _ := parseUint16Value(item["clid"])
		nick := item["client_nickname"]
		cid, _ := parseUint64Value(item["cid"])
		uid := item["client_unique_identifier"]
		clientType, _ := strconv.Atoi(item["client_type"])
		groupsStr := item["client_servergroups"]

		groups := make([]string, 0)
		if groupsStr != "" {
			groups = strings.Split(groupsStr, ",")
		}

		clients = append(clients, ClientInfo{
			ID:           clid,
			Nickname:     commands.Unescape(nick),
			ChannelID:    cid,
			UID:          uid,
			Type:         clientType,
			ServerGroups: groups,
		})
	}

	return clients, nil
}

// WaitConnected waits for the connection handshake to be completed.
// Returns nil on success, or the handshake/disconnect error on failure.
func (c *Client) WaitConnected(ctx context.Context) error {
	c.logger.Info("WaitConnected: waiting for handshake to complete")
	select {
	case <-c.connectedChan:
		c.logger.Info("WaitConnected: channel unblocked", slog.Any("err", c.connectedErr))
		return c.connectedErr
	case <-ctx.Done():
		c.logger.Warn("WaitConnected: context done", slog.Any("err", ctx.Err()))
		return ctx.Err()
	}
}

// ─── 服务器查询 ────────────────────────────────────────────

// GetServerInfo returns server details.
// 协议命令: serverinfo
func (c *Client) GetServerInfo() (*ServerInfo, error) {
	data, err := c.ExecCommandWithResponse("serverinfo", 5*time.Second)
	if err != nil {
		return nil, err
	}
	if len(data) == 0 {
		return nil, fmt.Errorf("serverinfo: %w", errNoDataReturnedForClient)
	}
	item := data[0]
	maxClients, _ := parseIntValue(item["virtualserver_maxclients"])
	clientsOnline, _ := parseIntValue(item["virtualserver_clientsonline"])
	channelsOnline, _ := parseIntValue(item["virtualserver_channelsonline"])
	uptime, _ := parseInt64Value(item["virtualserver_uptime"])
	created, _ := parseInt64Value(item["virtualserver_created"])
	iconID, _ := parseInt64Value(item["virtualserver_icon_id"])
	defServerGroup, _ := parseIntValue(item["virtualserver_default_server_group"])
	defChannelGroup, _ := parseIntValue(item["virtualserver_default_channel_group"])

	return &ServerInfo{
		Name:                commands.Unescape(item["virtualserver_name"]),
		WelcomeMessage:      commands.Unescape(item["virtualserver_welcomemessage"]),
		MaxClients:          maxClients,
		ClientsOnline:       clientsOnline,
		ChannelsOnline:      channelsOnline,
		Uptime:              uptime,
		Version:             item["virtualserver_version"],
		Platform:            item["virtualserver_platform"],
		Created:             created,
		IconID:              iconID,
		DefaultServerGroup:  defServerGroup,
		DefaultChannelGroup: defChannelGroup,
	}, nil
}

// ─── 频道查询 / 管理 ──────────────────────────────────────

// GetChannelInfo returns detailed info for a single channel.
// 协议命令: channelinfo cid=X
// 与 ListChannelsDetailed 的区别：本方法返回单频道的完整信息（含 description），
// ListChannelsDetailed 返回所有频道的部分属性。
func (c *Client) GetChannelInfo(channelID uint64) (*ChannelDetailInfo, error) {
	cmd := fmt.Sprintf("channelinfo cid=%d", channelID)
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return nil, err
	}
	if len(data) == 0 {
		return nil, fmt.Errorf("channelinfo: %w", errNoDataReturnedForClient)
	}
	item := data[0]
	cid, _ := parseUint64Value(item["cid"])
	pid, _ := parseUint64Value(item["channel_order"])
	order, _ := parseUint64Value(item["channel_order"])
	codec, _ := parseIntValue(item["channel_codec"])
	codecQuality, _ := parseIntValue(item["channel_codec_quality"])
	maxClients, _ := parseIntValue(item["channel_maxclients"])
	maxFamilyClients, _ := parseIntValue(item["channel_maxfamilyclients"])
	neededTalkPower, _ := parseIntValue(item["channel_needed_talk_power"])
	iconID, _ := parseInt64Value(item["channel_icon_id"])

	return &ChannelDetailInfo{
		ID:              cid,
		ParentID:        pid,
		Order:           order,
		Name:            commands.Unescape(item["channel_name"]),
		Topic:           commands.Unescape(item["channel_topic"]),
		Description:     commands.Unescape(item["channel_description"]),
		Codec:           codec,
		CodecQuality:    codecQuality,
		MaxClients:      maxClients,
		MaxFamilyClients: maxFamilyClients,
		NeededTalkPower: neededTalkPower,
		IconID:          iconID,
		IsPermanent:     parseBoolValue(item["channel_flag_permanent"]),
		IsSemiPermanent: parseBoolValue(item["channel_flag_semi_permanent"]),
		IsDefault:       parseBoolValue(item["channel_flag_default"]),
		IsPassword:      parseBoolValue(item["channel_flag_password"]),
		BannerGfxURL:    item["channel_banner_gfx_url"],
	}, nil
}

// FindChannels searches channels by name pattern.
// 协议命令: channelfind pattern=X
func (c *Client) FindChannels(pattern string) ([]ChannelInfo, error) {
	cmd := commands.BuildCommand("channelfind", map[string]string{"pattern": pattern})
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return nil, err
	}
	channels := make([]ChannelInfo, 0, len(data))
	for _, item := range data {
		cid, _ := parseUint64Value(item["cid"])
		channels = append(channels, ChannelInfo{
			ID:   cid,
			Name: commands.Unescape(item["channel_name"]),
		})
	}
	return channels, nil
}

// CreateChannel creates a new channel.
// 协议命令: channelcreate channel_name=X [params...]
// 返回新建频道的 ID。
// 常用可选参数通过 options map 传递，例如:
//   - "channel_topic": "主题"
//   - "channel_flag_permanent": "1"
//   - "channel_password": "密码"
//   - "cpid": "父频道ID"
func (c *Client) CreateChannel(name string, options map[string]string) (uint64, error) {
	params := [][2]string{{"channel_name", name}}
	for k, v := range options {
		params = append(params, [2]string{k, v})
	}
	cmd := commands.BuildCommandOrdered("channelcreate", params)
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return 0, err
	}
	if len(data) == 0 {
		return 0, fmt.Errorf("channelcreate: no cid returned")
	}
	cid, _ := parseUint64Value(data[0]["cid"])
	return cid, nil
}

// EditChannel modifies channel properties.
// 协议命令: channeledit cid=X [params...]
func (c *Client) EditChannel(channelID uint64, properties map[string]string) error {
	params := [][2]string{{"cid", fmt.Sprintf("%d", channelID)}}
	for k, v := range properties {
		params = append(params, [2]string{k, v})
	}
	cmd := commands.BuildCommandOrdered("channeledit", params)
	return c.ExecCommand(cmd, 5*time.Second)
}

// DeleteChannel deletes a channel.
// 协议命令: channeldelete cid=X force=X
// force=true 强制删除（含子频道）。
func (c *Client) DeleteChannel(channelID uint64, force bool) error {
	forceFlag := "0"
	if force {
		forceFlag = "1"
	}
	cmd := fmt.Sprintf("channeldelete cid=%d force=%s", channelID, forceFlag)
	return c.ExecCommand(cmd, 5*time.Second)
}

// MoveChannel moves a channel to a new parent or position.
// 协议命令: channelmove cid=X cpid=X order=X
func (c *Client) MoveChannel(channelID, parentID, order uint64) error {
	cmd := fmt.Sprintf("channelmove cid=%d cpid=%d order=%d", channelID, parentID, order)
	return c.ExecCommand(cmd, 5*time.Second)
}

// ─── 客户端查询 / 管理 ────────────────────────────────────

// GetClientDetailInfo returns extended client details.
// 协议命令: clientinfo clid=X
// 与 ListClients 返回的 ClientInfo 相比，本方法返回更丰富的属性
// （away 状态、平台、版本、IP、连接历史等）。
func (c *Client) GetClientDetailInfo(clid uint16) (*ClientDetailInfo, error) {
	cmd := fmt.Sprintf("clientinfo clid=%d", clid)
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return nil, err
	}
	if len(data) == 0 {
		return nil, fmt.Errorf("%w: %d", errNoDataReturnedForClient, clid)
	}
	item := data[0]
	cid, _ := parseUint16Value(item["clid"])
	channelID, _ := parseUint64Value(item["cid"])
	clientType, _ := parseIntValue(item["client_type"])
	created, _ := parseInt64Value(item["client_created"])
	lastConnected, _ := parseInt64Value(item["client_lastconnected"])
	totalConnections, _ := parseIntValue(item["client_totalconnections"])
	iconID, _ := parseInt64Value(item["client_icon_id"])

	groupsStr := item["client_servergroups"]
	groups := make([]string, 0)
	if groupsStr != "" {
		groups = strings.Split(groupsStr, ",")
	}

	return &ClientDetailInfo{
		ID:                 cid,
		Nickname:           commands.Unescape(item["client_nickname"]),
		UID:                item["client_unique_identifier"],
		ChannelID:          channelID,
		Type:               clientType,
		ServerGroups:       groups,
		Away:               parseBoolValue(item["client_away"]),
		AwayMessage:        commands.Unescape(item["client_away_message"]),
		InputMuted:         parseBoolValue(item["client_input_muted"]),
		OutputMuted:        parseBoolValue(item["client_output_muted"]),
		Platform:           item["client_platform"],
		Version:            item["client_version"],
		IP:                 item["connection_client_ip"],
		Created:            created,
		LastConnected:      lastConnected,
		TotalConnections:   totalConnections,
		Description:        commands.Unescape(item["client_description"]),
		IconID:             iconID,
	}, nil
}

// FindClientByDBID searches the client database by database ID.
// 协议命令: clientdbfind pattern=X -uid
// 返回客户端的 DBID 和 UID。
func (c *Client) FindClientByDBID(dbid uint64) (string, error) {
	cmd := fmt.Sprintf("clientdbfind -uid cldbid=%d", dbid)
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return "", err
	}
	if len(data) == 0 {
		return "", fmt.Errorf("clientdbfind: no result for cldbid %d", dbid)
	}
	return data[0]["client_unique_identifier"], nil
}

// FindClientByName searches the client database by nickname.
// 协议命令: clientdbfind pattern=X -uid
func (c *Client) FindClientByName(nickname string) (uid string, dbid uint64, err error) {
	cmd := commands.BuildCommand("clientdbfind", map[string]string{
		"pattern": nickname,
	}) + " -uid"
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return "", 0, err
	}
	if len(data) == 0 {
		return "", 0, fmt.Errorf("clientdbfind: no result for %q", nickname)
	}
	uid = data[0]["client_unique_identifier"]
	dbid, _ = parseUint64Value(data[0]["cldbid"])
	return uid, dbid, nil
}

// ListDBClients returns registered clients from the server database.
// 协议命令: clientdblist start=X duration=X
// start: 起始位置, duration: 返回数量（0=全部）
func (c *Client) ListDBClients(start, duration int) ([]DBClient, error) {
	cmd := fmt.Sprintf("clientdblist start=%d duration=%d", start, duration)
	data, err := c.ExecCommandWithResponse(cmd, 10*time.Second)
	if err != nil {
		return nil, err
	}
	clients := make([]DBClient, 0, len(data))
	for _, item := range data {
		dbid, _ := parseUint64Value(item["cldbid"])
		created, _ := parseInt64Value(item["client_created"])
		lastConnected, _ := parseInt64Value(item["client_lastconnected"])
		totalConnections, _ := parseIntValue(item["client_totalconnections"])
		clients = append(clients, DBClient{
			DBID:             dbid,
			UID:              item["client_unique_identifier"],
			Nickname:         commands.Unescape(item["client_nickname"]),
			Created:          created,
			LastConnected:    lastConnected,
			TotalConnections: totalConnections,
			Description:      commands.Unescape(item["client_description"]),
		})
	}
	return clients, nil
}

// UpdateSelf updates the current client's properties.
// 协议命令: clientupdate [params...]
// 常用参数:
//   - client_nickname: 新昵称
//   - client_away: "1"/"0"
//   - client_away_message: 离开消息
//   - client_input_muted: "1"/"0"
//   - client_output_muted: "1"/"0"
//   - client_phonetic_nickname: 语音昵称
//   - client_default_token: 默认 Token
func (c *Client) UpdateSelf(properties map[string]string) error {
	if len(properties) == 0 {
		return nil
	}
	cmd := commands.BuildCommand("clientupdate", properties)
	return c.ExecCommand(cmd, 5*time.Second)
}

// KickClient kicks a client from channel or server.
// 协议命令: clientkick clid=X reasonid=X reasonmsg=X
// reasonid: 4=从频道踢出, 5=从服务器踢出
func (c *Client) KickClient(clid uint16, reasonID int, reasonMsg string) error {
	cmd := commands.BuildCommandOrdered("clientkick", [][2]string{
		{"clid", fmt.Sprintf("%d", clid)},
		{"reasonid", fmt.Sprintf("%d", reasonID)},
		{"reasonmsg", reasonMsg},
	})
	return c.ExecCommand(cmd, 5*time.Second)
}

// ─── Ban 管理 ────────────────────────────────────────────

// ListBans returns all active ban entries.
// 协议命令: banlist
func (c *Client) ListBans() ([]BanEntry, error) {
	data, err := c.ExecCommandWithResponse("banlist", 5*time.Second)
	if err != nil {
		return nil, err
	}
	bans := make([]BanEntry, 0, len(data))
	for _, item := range data {
		banID, _ := parseInt64Value(item["banid"])
		created, _ := parseInt64Value(item["created"])
		bans = append(bans, BanEntry{
			BanID:       banID,
			IP:          item["ip"],
			Name:        commands.Unescape(item["name"]),
			UID:         item["uid"],
			Created:     created,
			InvokerName: commands.Unescape(item["invokername"]),
			InvokerUID:  item["invokeruid"],
			Reason:      commands.Unescape(item["reason"]),
			Enforcement: parseBoolValue(item["enforcement"]),
		})
	}
	return bans, nil
}

// AddBan adds a ban entry.
// 协议命令: banadd ip=X name=X uid=X time=X banreason=X
// ip/name/uid 至少指定一个；time 单位为秒，0=永久。
func (c *Client) AddBan(ip, name, uid string, timeSeconds int, reason string) error {
	params := make([][2]string, 0, 5)
	if ip != "" {
		params = append(params, [2]string{"ip", ip})
	}
	if name != "" {
		params = append(params, [2]string{"name", name})
	}
	if uid != "" {
		params = append(params, [2]string{"uid", uid})
	}
	if timeSeconds > 0 {
		params = append(params, [2]string{"time", fmt.Sprintf("%d", timeSeconds)})
	}
	if reason != "" {
		params = append(params, [2]string{"banreason", reason})
	}
	cmd := commands.BuildCommandOrdered("banadd", params)
	return c.ExecCommand(cmd, 5*time.Second)
}

// DeleteBan removes a ban entry.
// 协议命令: bandel banid=X
func (c *Client) DeleteBan(banID int64) error {
	cmd := fmt.Sprintf("bandel banid=%d", banID)
	return c.ExecCommand(cmd, 5*time.Second)
}

// DeleteAllBans removes all ban entries.
// 协议命令: bandelall
func (c *Client) DeleteAllBans() error {
	return c.ExecCommand("bandelall", 5*time.Second)
}

// ─── 文件列表 ────────────────────────────────────────────

// ListFiles returns files and directories in a channel path.
// 协议命令: ftgetfilelist cid=X path=X
// path 为频道内虚拟路径，根目录为 "/"。
func (c *Client) ListFiles(channelID uint64, path string) ([]FileEntry, error) {
	cmd := commands.BuildCommandOrdered("ftgetfilelist", [][2]string{
		{"cid", fmt.Sprintf("%d", channelID)},
		{"path", path},
	})
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return nil, err
	}
	files := make([]FileEntry, 0, len(data))
	for _, item := range data {
		size, _ := parseUint64Value(item["size"])
		dt, _ := parseInt64Value(item["datetime"])
		files = append(files, FileEntry{
			Name:     commands.Unescape(item["name"]),
			Size:     size,
			DateTime: dt,
			IsFile:   parseBoolValue(item["is_file"]),
		})
	}
	return files, nil
}

// ─── Token 管理 ──────────────────────────────────────────

// ListTokens returns all privilege keys.
// 协议命令: tokenlist
func (c *Client) ListTokens() ([]TokenEntry, error) {
	data, err := c.ExecCommandWithResponse("tokenlist", 5*time.Second)
	if err != nil {
		return nil, err
	}
	tokens := make([]TokenEntry, 0, len(data))
	for _, item := range data {
		tokenType, _ := parseIntValue(item["token_type"])
		tokenID1, _ := parseInt64Value(item["token_id1"])
		tokenID2, _ := parseInt64Value(item["token_id2"])
		created, _ := parseInt64Value(item["token_created"])
		tokens = append(tokens, TokenEntry{
			Token:       item["token"],
			TokenType:   tokenType,
			TokenID1:    tokenID1,
			TokenID2:    tokenID2,
			Created:     created,
			Description: commands.Unescape(item["token_description"]),
		})
	}
	return tokens, nil
}

// UseToken activates a privilege key.
// 协议命令: tokenuse token=X
func (c *Client) UseToken(token string) error {
	cmd := commands.BuildCommand("tokenuse", map[string]string{"token": token})
	return c.ExecCommand(cmd, 5*time.Second)
}

// ─── 投诉管理 ────────────────────────────────────────────

// ListComplaints returns complaints against a target client.
// 协议命令: complainlist tcldbid=X
// 若 targetDBID=0 则返回全部投诉。
func (c *Client) ListComplaints(targetDBID uint64) ([]ComplaintEntry, error) {
	cmd := "complainlist"
	if targetDBID > 0 {
		cmd = fmt.Sprintf("complainlist tcldbid=%d", targetDBID)
	}
	data, err := c.ExecCommandWithResponse(cmd, 5*time.Second)
	if err != nil {
		return nil, err
	}
	complaints := make([]ComplaintEntry, 0, len(data))
	for _, item := range data {
		fromDBID, _ := parseUint64Value(item["fcldbid"])
		toDBID, _ := parseUint64Value(item["tcldbid"])
		ts, _ := parseInt64Value(item["timestamp"])
		complaints = append(complaints, ComplaintEntry{
			FromDBID:  fromDBID,
			ToDBID:    toDBID,
			Message:   commands.Unescape(item["message"]),
			Timestamp: ts,
		})
	}
	return complaints, nil
}

// AddComplaint files a complaint against a target client.
// 协议命令: complainadd tcldbid=X message=X
func (c *Client) AddComplaint(targetDBID uint64, message string) error {
	cmd := commands.BuildCommandOrdered("complainadd", [][2]string{
		{"tcldbid", fmt.Sprintf("%d", targetDBID)},
		{"message", message},
	})
	return c.ExecCommand(cmd, 5*time.Second)
}

// DeleteComplaint removes a complaint.
// 协议命令: complaindel tcldbid=X fcldbid=X
func (c *Client) DeleteComplaint(targetDBID, fromDBID uint64) error {
	cmd := fmt.Sprintf("complaindel tcldbid=%d fcldbid=%d", targetDBID, fromDBID)
	return c.ExecCommand(cmd, 5*time.Second)
}
