package viral

import (
	"sort"
	"strings"

	missionentity "haixun-backend/internal/model/copy_mission/domain/entity"
)

// MergeSimilarAccounts merges previous and newly discovered similar accounts
// keyed by lowercase username. Newly discovered accounts overwrite per-account
// statistics, but pinned/excluded status and previously seen accounts are
// preserved. Pinned accounts stay at the top even when not refreshed.
func MergeSimilarAccounts(prev, next []missionentity.SimilarAccount) []missionentity.SimilarAccount {
	if len(prev) == 0 {
		return capSimilarAccounts(append([]missionentity.SimilarAccount(nil), next...))
	}
	if len(next) == 0 {
		return prioritizePinnedSimilarAccounts(append([]missionentity.SimilarAccount(nil), prev...))
	}

	byUser := map[string]missionentity.SimilarAccount{}
	newOrder := make([]string, 0, len(next))
	for _, item := range next {
		key := strings.ToLower(strings.TrimSpace(item.Username))
		if key == "" {
			continue
		}
		if _, ok := byUser[key]; !ok {
			newOrder = append(newOrder, key)
		}
		byUser[key] = mergeAccountRecord(byUser[key], item)
	}

	prevOrder := make([]string, 0, len(prev))
	for _, item := range prev {
		key := strings.ToLower(strings.TrimSpace(item.Username))
		if key == "" {
			continue
		}
		if _, ok := byUser[key]; ok {
			byUser[key] = mergeAccountRecord(item, byUser[key])
			continue
		}
		byUser[key] = item
		prevOrder = append(prevOrder, key)
	}

	out := make([]missionentity.SimilarAccount, 0, len(newOrder)+len(prevOrder))
	for _, key := range newOrder {
		if acc, ok := byUser[key]; ok {
			out = append(out, acc)
		}
	}
	for _, key := range prevOrder {
		if acc, ok := byUser[key]; ok {
			out = append(out, acc)
		}
	}
	return prioritizePinnedSimilarAccounts(out)
}

func mergeAccountRecord(prev, next missionentity.SimilarAccount) missionentity.SimilarAccount {
	out := next
	out.MatchedSource = unionMatchedSource(prev.MatchedSource, next.MatchedSource)
	switch prev.Status {
	case missionentity.SimilarAccountStatusPinned, missionentity.SimilarAccountStatusExcluded:
		out.Status = prev.Status
	default:
		if strings.TrimSpace(out.Status) == "" {
			out.Status = missionentity.SimilarAccountStatusRecommended
		}
	}
	return out
}

func prioritizePinnedSimilarAccounts(accounts []missionentity.SimilarAccount) []missionentity.SimilarAccount {
	if len(accounts) == 0 {
		return nil
	}
	pinned := make([]missionentity.SimilarAccount, 0)
	rest := make([]missionentity.SimilarAccount, 0, len(accounts))
	for _, item := range accounts {
		if item.Status == missionentity.SimilarAccountStatusPinned {
			pinned = append(pinned, item)
		} else {
			rest = append(rest, item)
		}
	}
	return capSimilarAccounts(append(pinned, rest...))
}

func capSimilarAccounts(accounts []missionentity.SimilarAccount) []missionentity.SimilarAccount {
	if len(accounts) <= MaxSimilarAccounts {
		return accounts
	}
	pinned := make([]missionentity.SimilarAccount, 0)
	rest := make([]missionentity.SimilarAccount, 0, len(accounts))
	for _, item := range accounts {
		if item.Status == missionentity.SimilarAccountStatusPinned {
			pinned = append(pinned, item)
		} else {
			rest = append(rest, item)
		}
	}
	remaining := MaxSimilarAccounts - len(pinned)
	if remaining < 0 {
		return pinned[:MaxSimilarAccounts]
	}
	if len(rest) > remaining {
		rest = rest[:remaining]
	}
	return append(pinned, rest...)
}

func unionMatchedSource(a, b []string) []string {
	if len(a) == 0 && len(b) == 0 {
		return nil
	}
	seen := map[string]struct{}{}
	out := make([]string, 0, len(a)+len(b))
	for _, item := range append(append([]string{}, a...), b...) {
		s := strings.TrimSpace(item)
		if s == "" {
			continue
		}
		if _, ok := seen[s]; ok {
			continue
		}
		seen[s] = struct{}{}
		out = append(out, s)
	}
	sort.Strings(out)
	if len(out) == 0 {
		return nil
	}
	return out
}

func ExcludedSimilarAccountUsernames(accounts []missionentity.SimilarAccount) []string {
	out := make([]string, 0)
	seen := map[string]struct{}{}
	for _, item := range accounts {
		if item.Status != missionentity.SimilarAccountStatusExcluded {
			continue
		}
		user := strings.TrimSpace(item.Username)
		if user == "" {
			continue
		}
		key := strings.ToLower(user)
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, user)
	}
	return out
}

func SimilarAccountUsernames(accounts []missionentity.SimilarAccount) []string {
	out := make([]string, 0, len(accounts))
	seen := map[string]struct{}{}
	for _, item := range accounts {
		user := strings.TrimSpace(item.Username)
		if user == "" {
			continue
		}
		key := strings.ToLower(user)
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, user)
	}
	return out
}

func isExcluded(excluded []string, username string) bool {
	user := strings.ToLower(strings.TrimSpace(username))
	if user == "" {
		return false
	}
	for _, item := range excluded {
		if strings.ToLower(strings.TrimSpace(item)) == user {
			return true
		}
	}
	return false
}
