package eventstore

import (
	"context"
	"fmt"

	"github.com/fiatjaf/eventstore/internal"
	"github.com/nbd-wtf/go-nostr"
)

type RelayWrapper struct {
	Store
}

var _ nostr.RelayStore = (*RelayWrapper)(nil)

func (w RelayWrapper) Publish(ctx context.Context, evt nostr.Event) error {
	if nostr.IsEphemeralKind(evt.Kind) {
		// do not store ephemeral events
		return nil
	}

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	if nostr.IsRegularKind(evt.Kind) {
		// regular events are just saved directly
		if err := w.SaveEvent(ctx, &evt); err != nil && err != ErrDupEvent {
			return fmt.Errorf("failed to save: %w", err)
		}
		return nil
	}

	// from now on we know they are replaceable or addressable
	if replacer, ok := w.Store.(Replacer); ok {
		// use the replacer interface to potentially reduce queries and race conditions
		replacer.Replace(ctx, &evt)
	} else {
		// otherwise do it the manual way
		filter := nostr.Filter{Limit: 1, Kinds: []int{evt.Kind}, Authors: []string{evt.PubKey}}
		if nostr.IsAddressableKind(evt.Kind) {
			// when addressable, add the "d" tag to the filter
			filter.Tags = nostr.TagMap{"d": []string{evt.Tags.GetD()}}
		}

		// now we fetch the past events, whatever they are, delete them and then save the new
		ch, err := w.Store.QueryEvents(ctx, filter)
		if err != nil {
			return fmt.Errorf("failed to query before replacing: %w", err)
		}

		shouldStore := true
		for previous := range ch {
			if internal.IsOlder(previous, &evt) {
				if err := w.Store.DeleteEvent(ctx, previous); err != nil {
					return fmt.Errorf("failed to delete event for replacing: %w", err)
				}
			} else {
				// there is a newer event already stored, so we won't store this
				shouldStore = false
			}
		}
		if shouldStore {
			if err := w.SaveEvent(ctx, &evt); err != nil && err != ErrDupEvent {
				return fmt.Errorf("failed to save: %w", err)
			}
		}
	}

	return nil
}

func (w RelayWrapper) QuerySync(ctx context.Context, filter nostr.Filter) ([]*nostr.Event, error) {
	ch, err := w.Store.QueryEvents(ctx, filter)
	if err != nil {
		return nil, fmt.Errorf("failed to query: %w", err)
	}

	n := filter.Limit
	if n == 0 {
		n = 500
	}

	results := make([]*nostr.Event, 0, n)
	for evt := range ch {
		results = append(results, evt)
	}

	return results, nil
}