package tools import ( "context" "sort" "strings" "isphere-ai-bridge/internal/isphere" "isphere-ai-bridge/internal/msglib" ) type DisplayEntitySource interface { DisplayEntities(ctx context.Context, opts msglib.DisplayEntitiesOptions) ([]msglib.DisplayEntity, error) } func displayEntityQueryLimit(limit int) int { if limit <= 0 { return msglib.DefaultDisplayEntityLimit } if limit > msglib.MaxDisplayEntityLimit { return msglib.MaxDisplayEntityLimit } return limit } func contactsFromDisplayEntities(entities []msglib.DisplayEntity) []isphere.Contact { contacts := make([]isphere.Contact, 0, len(entities)) for _, entity := range entities { id := strings.TrimSpace(entity.JID) name := strings.TrimSpace(entity.DisplayName) if id == "" { id = name } if name == "" { name = id } if id == "" { continue } contacts = append(contacts, isphere.Contact{ ContactID: id, DisplayName: name, Account: strings.TrimSpace(entity.JID), Source: "msglib_readonly", Confidence: entity.Confidence, RawRef: "msglib:" + entity.SourceTable, }) } return contacts } func groupsFromDisplayEntities(entities []msglib.DisplayEntity) []isphere.Group { groups := make([]isphere.Group, 0, len(entities)) for _, entity := range entities { id := strings.TrimSpace(entity.JID) name := strings.TrimSpace(entity.DisplayName) if id == "" { id = name } if name == "" { name = id } if id == "" { continue } groups = append(groups, isphere.Group{ GroupID: id, DisplayName: name, Source: "msglib_readonly", Confidence: entity.Confidence, RawRef: "msglib:" + entity.SourceTable, }) } return groups } func mergeContacts(primary []isphere.Contact, fallback []isphere.Contact, query string, limit int) isphere.SearchContactsResult { seen := map[string]bool{} contacts := make([]isphere.Contact, 0, len(primary)+len(fallback)) for _, contact := range append(primary, fallback...) { id := strings.TrimSpace(contact.ContactID) if id == "" || seen[strings.ToLower(id)] { continue } seen[strings.ToLower(id)] = true contacts = append(contacts, contact) } sortContactsForQuery(contacts, query) if limit > 0 && len(contacts) > limit { contacts = contacts[:limit] } return isphere.SearchContactsResult{Contacts: contacts} } func mergeGroups(primary []isphere.Group, fallback []isphere.Group, query string, limit int) isphere.SearchGroupsResult { seen := map[string]bool{} groups := make([]isphere.Group, 0, len(primary)+len(fallback)) for _, group := range append(primary, fallback...) { id := strings.TrimSpace(group.GroupID) if id == "" || seen[strings.ToLower(id)] { continue } seen[strings.ToLower(id)] = true groups = append(groups, group) } sortGroupsForQuery(groups, query) if limit > 0 && len(groups) > limit { groups = groups[:limit] } return isphere.SearchGroupsResult{Groups: groups} } func sortContactsForQuery(contacts []isphere.Contact, query string) { queryText := strings.ToLower(strings.TrimSpace(query)) sort.Slice(contacts, func(i, j int) bool { leftRank := contactRankForQuery(contacts[i], queryText) rightRank := contactRankForQuery(contacts[j], queryText) if leftRank != rightRank { return leftRank < rightRank } leftID := strings.ToLower(contacts[i].ContactID) rightID := strings.ToLower(contacts[j].ContactID) if leftID != rightID { return leftID < rightID } return contacts[i].ContactID < contacts[j].ContactID }) } func contactRankForQuery(contact isphere.Contact, queryText string) int { if queryText == "" { return 0 } values := []string{ strings.ToLower(contact.ContactID), strings.ToLower(contact.DisplayName), strings.ToLower(contact.Account), } for _, value := range values { if value == queryText { return 0 } } for _, value := range values { if strings.HasPrefix(value, queryText) { return 1 } } return 2 } func sortGroupsForQuery(groups []isphere.Group, query string) { queryText := strings.ToLower(strings.TrimSpace(query)) sort.Slice(groups, func(i, j int) bool { leftRank := groupRankForQuery(groups[i], queryText) rightRank := groupRankForQuery(groups[j], queryText) if leftRank != rightRank { return leftRank < rightRank } leftID := strings.ToLower(groups[i].GroupID) rightID := strings.ToLower(groups[j].GroupID) if leftID != rightID { return leftID < rightID } return groups[i].GroupID < groups[j].GroupID }) } func groupRankForQuery(group isphere.Group, queryText string) int { if queryText == "" { return 0 } values := []string{ strings.ToLower(group.GroupID), strings.ToLower(group.DisplayName), } for _, value := range values { if value == queryText { return 0 } } for _, value := range values { if strings.HasPrefix(value, queryText) { return 1 } } return 2 }