package app

import (
	"context"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/core/runeffect"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

func (adapter *serviceEventDeliveryDispatcher) reserveServiceEventDelivery(ctx context.Context, tx coredb.Tx, candidate serviceEventDeliveryCandidate) (serviceactor.EventDelivery, bool, error) {
	return adapter.store.serviceActorStore().InTx(tx).ReserveEventDelivery(ctx, serviceactor.EventDeliveryReserve{
		WorkspaceID:         candidate.Subscription.WorkspaceID,
		EventSubscriptionID: candidate.Subscription.ID,
		WebhookReceiptID:    adapter.receiptID,
		EventType:           adapter.event.EventType,
		EventAction:         providerEventActionSnapshot(adapter.event.Event),
		EventID:             adapter.event.Event.EventID,
		EventKey:            buildServiceEventSourceOccurrenceRef(adapter.event),
		EventPayload:        adapter.normalizedWebhookEventPayload(),
		ExternalResourceRef: adapter.event.Event.ResourceIdentifier,
		ExternalResourceURL: adapter.event.Event.ResourceURL,
		IdempotencyKey:      serviceEventStableKey(candidate.Subscription.ID, adapter.event.Installation.WorkspaceID, adapter.event.Event.EventID),
		Now:                 adapter.now,
	})
}

func (adapter *serviceEventDeliveryDispatcher) completeServiceEventDeliveryInTx(ctx context.Context, tx coredb.Tx, candidate serviceEventDeliveryCandidate, effect runeffect.Effect) error {
	if strings.TrimSpace(candidate.ServiceEventDeliveryID) == "" {
		return nil
	}
	return adapter.store.serviceActorStore().InTx(tx).CompleteEventDelivery(ctx, serviceactor.EventDeliveryCompletion{
		ID:     candidate.ServiceEventDeliveryID,
		Status: runeffect.StatusSucceeded,
		Effect: effect,
		Now:    adapter.now,
	})
}

func (adapter *serviceEventDeliveryDispatcher) failServiceEventDeliveryInTx(ctx context.Context, tx coredb.Tx, candidate serviceEventDeliveryCandidate, code string, detail string) error {
	if strings.TrimSpace(candidate.ServiceEventDeliveryID) == "" {
		return nil
	}
	return adapter.store.serviceActorStore().InTx(tx).CompleteEventDelivery(ctx, serviceactor.EventDeliveryCompletion{
		ID:     candidate.ServiceEventDeliveryID,
		Status: runeffect.StatusFailed,
		Effect: runeffect.NoEffect(),
		Error:  runeffect.Error{Code: code, Detail: detail},
		Now:    adapter.now,
	})
}

func (adapter *serviceEventDeliveryDispatcher) reserveAndFailServiceEventDeliveryInTx(ctx context.Context, tx coredb.Tx, candidate serviceEventDeliveryCandidate, code string, detail string) error {
	delivery, duplicate, err := adapter.reserveServiceEventDelivery(ctx, tx, candidate)
	if err != nil {
		return err
	}
	if duplicate {
		return nil
	}
	candidate.ServiceEventDeliveryID = delivery.ID
	return adapter.failServiceEventDeliveryInTx(ctx, tx, candidate, code, detail)
}
