package maildir

import (
	"context"
	"runtime"
	"sync"

	"git.sr.ht/~rjarry/aerc/lib/log"
	"git.sr.ht/~rjarry/aerc/models"
	"git.sr.ht/~rjarry/aerc/worker/lib"
	"git.sr.ht/~rjarry/aerc/worker/types"
)

func (w *Worker) search(ctx context.Context, criteria *types.SearchCriteria) ([]models.UID, error) {
	criteria.PrepareHeader()
	requiredParts := lib.GetRequiredParts(criteria)
	w.worker.Debugf("Required parts bitmask for search: %b", requiredParts)

	keys, err := w.c.UIDs(*w.selected)
	if err != nil {
		return nil, err
	}

	var matchedUids []models.UID
	mu := sync.Mutex{}
	wg := sync.WaitGroup{}
	// Hard limit at 2x CPU cores
	max := runtime.NumCPU() * 2
	limit := make(chan struct{}, max)
	for _, key := range keys {
		select {
		case <-ctx.Done():
			return nil, context.Canceled
		default:
			limit <- struct{}{}
			wg.Add(1)
			go func(key models.UID) {
				defer log.PanicHandler()
				defer wg.Done()
				success, err := w.searchKey(key, criteria, requiredParts)
				if err != nil {
					// don't return early so that we can still get some results
					w.worker.Errorf("Failed to search key %d: %v", key, err)
				} else if success {
					mu.Lock()
					matchedUids = append(matchedUids, key)
					mu.Unlock()
				}
				<-limit
			}(key)

		}
	}
	wg.Wait()
	return matchedUids, nil
}

// Execute the search criteria for the given key, returns true if search succeeded
func (w *Worker) searchKey(key models.UID, criteria *types.SearchCriteria,
	parts lib.MsgParts,
) (bool, error) {
	message, err := w.c.Message(*w.selected, key)
	if err != nil {
		return false, err
	}
	return lib.SearchMessage(message, criteria, parts)
}
