package mmm

import (
	"fmt"
	"math"
	"runtime"

	"fiatjaf.com/nostr"
	"fiatjaf.com/nostr/eventstore/internal"
	"github.com/PowerDNS/lmdb-go/lmdb"
)

func (il *IndexingLayer) ReplaceEvent(evt nostr.Event) error {
	// sanity checking
	if evt.CreatedAt > math.MaxUint32 || evt.Kind > math.MaxUint16 {
		return fmt.Errorf("event with values out of expected boundaries")
	}

	il.mmmm.writeMutex.Lock()
	defer il.mmmm.writeMutex.Unlock()

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	filter := nostr.Filter{Limit: 1, Kinds: []nostr.Kind{evt.Kind}, Authors: []nostr.PubKey{evt.PubKey}}
	if evt.Kind.IsAddressable() {
		// when addressable, add the "d" tag to the filter
		filter.Tags = nostr.TagMap{"d": []string{evt.Tags.GetD()}}
	}

	return il.mmmm.lmdbEnv.Update(func(mmmtxn *lmdb.Txn) error {
		mmmtxn.RawRead = true

		return il.lmdbEnv.Update(func(iltxn *lmdb.Txn) error {
			// now we fetch the past events, whatever they are, delete them and then save the new
			prevResults, err := il.query(iltxn, filter, 10) // in theory limit could be just 1 and this should work
			if err != nil {
				return fmt.Errorf("failed to query past events with %s: %w", filter, err)
			}

			shouldStore := true
			for _, previous := range prevResults {
				if internal.IsOlder(previous.Event, evt) {
					if err := il.delete(mmmtxn, iltxn, previous.Event.ID); err != nil {
						return fmt.Errorf("failed to delete event %s for replacing: %w", previous.Event.ID, err)
					}
				} else {
					// there is a newer event already stored, so we won't store this
					shouldStore = false
				}
			}
			if shouldStore {
				_, err := il.mmmm.storeOn(mmmtxn, []*IndexingLayer{il}, []*lmdb.Txn{iltxn}, evt)
				return err
			}

			return nil
		})
	})
}
