package feishu

import (
	"context"
	"fmt"
	"strings"
	"time"

	lark "github.com/larksuite/oapi-sdk-go/v3"
	larkcontact "github.com/larksuite/oapi-sdk-go/v3/service/contact/v3"
	larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"

	"github.com/memohai/memoh/internal/channel"
)

type feishuSenderProfile struct {
	displayName string
	username    string
}

const feishuChatMembersPageSize = 100

// enrichSenderProfile fills sender display name / username for inbound messages.
// It first tries Contact.User.Get (open_id/user_id), then falls back to group member
// lookup when permissions are limited.
func (a *FeishuAdapter) enrichSenderProfile(ctx context.Context, cfg channel.ChannelConfig, event *larkim.P2MessageReceiveV1, msg *channel.InboundMessage) {
	if msg == nil {
		return
	}
	needDisplay := strings.TrimSpace(msg.Sender.DisplayName) == "" &&
		strings.TrimSpace(msg.Sender.Attribute("display_name")) == "" &&
		strings.TrimSpace(msg.Sender.Attribute("name")) == ""
	needUsername := strings.TrimSpace(msg.Sender.Attribute("username")) == ""
	if !needDisplay && !needUsername {
		return
	}

	openID := strings.TrimSpace(msg.Sender.Attribute("open_id"))
	userID := strings.TrimSpace(msg.Sender.Attribute("user_id"))
	if openID == "" && userID == "" {
		return
	}

	chatID := ""
	if event != nil && event.Event != nil && event.Event.Message != nil && event.Event.Message.ChatId != nil {
		chatID = strings.TrimSpace(*event.Event.Message.ChatId)
	}

	if ctx == nil {
		ctx = context.Background()
	}
	lookupCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	profile, err := a.lookupSenderProfile(lookupCtx, cfg, openID, userID, chatID)
	if err != nil {
		if a.logger != nil {
			a.logger.Debug("feishu sender profile lookup failed",
				"config_id", cfg.ID,
				"open_id", openID,
				"user_id", userID,
				"chat_id", chatID,
				"error", err,
			)
		}
	}
	if strings.TrimSpace(profile.displayName) == "" && strings.TrimSpace(profile.username) == "" {
		profile = fallbackSenderProfile(openID, userID)
	}
	applySenderProfile(msg, profile)
}

func (a *FeishuAdapter) lookupSenderProfile(ctx context.Context, cfg channel.ChannelConfig, openID, userID, chatID string) (feishuSenderProfile, error) {
	feishuCfg, err := parseConfig(cfg.Credentials)
	if err != nil {
		return feishuSenderProfile{}, err
	}
	client := lark.NewClient(feishuCfg.AppID, feishuCfg.AppSecret, lark.WithOpenBaseUrl(feishuCfg.openBaseURL()))

	var lastErr error
	chatID = strings.TrimSpace(chatID)
	if strings.HasPrefix(chatID, "chat_id:") {
		chatID = strings.TrimPrefix(chatID, "chat_id:")
	}

	// Group scene: chat members has the highest chance to return a human-readable name.
	if chatID != "" && openID != "" {
		if profile, err := lookupSenderProfileFromGroupMember(ctx, client, chatID, "open_id", openID); err == nil {
			if strings.TrimSpace(profile.displayName) != "" || strings.TrimSpace(profile.username) != "" {
				return profile, nil
			}
		} else {
			lastErr = err
		}
	}
	if chatID != "" && userID != "" {
		if profile, err := lookupSenderProfileFromGroupMember(ctx, client, chatID, "user_id", userID); err == nil {
			if strings.TrimSpace(profile.displayName) != "" || strings.TrimSpace(profile.username) != "" {
				return profile, nil
			}
		} else {
			lastErr = err
		}
	}

	if profile, err := lookupSenderProfileFromContact(ctx, client, openID, userID); err == nil {
		if strings.TrimSpace(profile.displayName) != "" || strings.TrimSpace(profile.username) != "" {
			return profile, nil
		}
	} else {
		lastErr = err
	}

	if lastErr != nil {
		return feishuSenderProfile{}, lastErr
	}
	return feishuSenderProfile{}, nil
}

func lookupSenderProfileFromContact(ctx context.Context, client *lark.Client, openID, userID string) (feishuSenderProfile, error) {
	lookupID := strings.TrimSpace(openID)
	idType := larkcontact.UserIdTypeOpenId
	if lookupID == "" {
		lookupID = strings.TrimSpace(userID)
		idType = larkcontact.UserIdTypeUserId
	}
	if lookupID == "" {
		return feishuSenderProfile{}, fmt.Errorf("empty sender id")
	}
	req := larkcontact.NewGetUserReqBuilder().
		UserIdType(idType).
		UserId(lookupID).
		Build()
	resp, err := client.Contact.User.Get(ctx, req)
	if err != nil {
		return feishuSenderProfile{}, err
	}
	if resp == nil || !resp.Success() {
		code := 0
		msg := ""
		if resp != nil {
			code = resp.Code
			msg = strings.TrimSpace(resp.Msg)
		}
		return feishuSenderProfile{}, fmt.Errorf("feishu get user failed: code=%d msg=%s", code, msg)
	}
	if resp.Data == nil || resp.Data.User == nil {
		return feishuSenderProfile{}, fmt.Errorf("feishu get user returned empty user")
	}
	displayName := ptrStr(resp.Data.User.Name)
	username := ptrStr(resp.Data.User.Nickname)
	if username == "" {
		username = displayName
	}
	return feishuSenderProfile{
		displayName: displayName,
		username:    username,
	}, nil
}

func lookupSenderProfileFromGroupMember(ctx context.Context, client *lark.Client, chatID, memberIDType, memberID string) (feishuSenderProfile, error) {
	memberIDType = strings.TrimSpace(memberIDType)
	memberID = strings.TrimSpace(memberID)
	if memberIDType == "" || memberID == "" {
		return feishuSenderProfile{}, fmt.Errorf("empty member lookup input")
	}
	pageToken := ""
	for page := 0; page < 5; page++ {
		builder := larkim.NewGetChatMembersReqBuilder().
			ChatId(chatID).
			MemberIdType(memberIDType).
			PageSize(feishuChatMembersPageSize)
		if pageToken != "" {
			builder = builder.PageToken(pageToken)
		}
		resp, err := client.Im.ChatMembers.Get(ctx, builder.Build())
		if err != nil {
			return feishuSenderProfile{}, err
		}
		if resp == nil || !resp.Success() {
			code := 0
			msg := ""
			if resp != nil {
				code = resp.Code
				msg = strings.TrimSpace(resp.Msg)
			}
			return feishuSenderProfile{}, fmt.Errorf("feishu get chat members failed: code=%d msg=%s", code, msg)
		}
		if resp.Data == nil {
			return feishuSenderProfile{}, nil
		}
		for _, item := range resp.Data.Items {
			if item == nil {
				continue
			}
			if strings.TrimSpace(ptrStr(item.MemberId)) != memberID {
				continue
			}
			name := ptrStr(item.Name)
			username := firstNameFallback(name)
			if username == "" {
				username = name
			}
			return feishuSenderProfile{
				displayName: name,
				username:    username,
			}, nil
		}
		hasMore := resp.Data.HasMore != nil && *resp.Data.HasMore
		if !hasMore || resp.Data.PageToken == nil {
			break
		}
		pageToken = strings.TrimSpace(*resp.Data.PageToken)
		if pageToken == "" {
			break
		}
	}
	return feishuSenderProfile{}, nil
}

func fallbackSenderProfile(openID, userID string) feishuSenderProfile {
	openID = strings.TrimSpace(openID)
	userID = strings.TrimSpace(userID)
	username := userID
	if username == "" {
		username = openID
	}
	if username == "" {
		return feishuSenderProfile{}
	}
	displayName := username
	return feishuSenderProfile{
		displayName: displayName,
		username:    username,
	}
}

func firstNameFallback(name string) string {
	name = strings.TrimSpace(name)
	if name == "" {
		return ""
	}
	parts := strings.Fields(name)
	if len(parts) == 0 {
		return ""
	}
	return strings.TrimSpace(parts[0])
}

func applySenderProfile(msg *channel.InboundMessage, profile feishuSenderProfile) {
	if msg == nil {
		return
	}
	displayName := strings.TrimSpace(profile.displayName)
	username := strings.TrimSpace(profile.username)
	if username == "" {
		username = displayName
	}
	if msg.Sender.Attributes == nil {
		msg.Sender.Attributes = map[string]string{}
	}
	if displayName != "" {
		if strings.TrimSpace(msg.Sender.DisplayName) == "" {
			msg.Sender.DisplayName = displayName
		}
		if strings.TrimSpace(msg.Sender.Attributes["display_name"]) == "" {
			msg.Sender.Attributes["display_name"] = displayName
		}
		if strings.TrimSpace(msg.Sender.Attributes["name"]) == "" {
			msg.Sender.Attributes["name"] = displayName
		}
	}
	if username != "" && strings.TrimSpace(msg.Sender.Attributes["username"]) == "" {
		msg.Sender.Attributes["username"] = username
	}
}
