package app

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

func (store *Store) persistScopeFanoutPayloads(ctx context.Context, tx coredb.Tx, delivery message.MessageDelivery) error {
	if strings.TrimSpace(delivery.MessageID) != "" && !delivery.IdempotentReplay {
		if delivery.AppMessage == nil {
			return fmt.Errorf("scope fanout payload missing app message for %s", delivery.MessageID)
		}
		payload, err := message.MarshalScopeFanoutPayload(*delivery.AppMessage, delivery)
		if err != nil {
			return err
		}
		if err := message.NewScopeRepositories(tx).Outbox.SetPayloadForMessage(ctx, delivery.MessageID, payload); err != nil {
			return err
		}
	}
	for _, followup := range delivery.FollowupDeliveries {
		if err := store.persistScopeFanoutPayloads(ctx, tx, followup); err != nil {
			return err
		}
	}
	return nil
}

func (store *Store) MarkScopeFanoutDeliveredForMessage(ctx context.Context, messageID string) error {
	return message.NewScopeRepositories(store.db).Outbox.MarkDeliveredForMessage(ctx, messageID, time.Now().UTC())
}

func (store *Store) ClaimScopeFanoutForMessage(
	ctx context.Context,
	messageID string,
	leaseOwner string,
	leaseUntil time.Time,
) (message.ScopeFanoutOutboxRecord, bool, error) {
	return message.NewScopeRepositories(store.db).Outbox.ClaimForMessageInline(
		ctx,
		messageID,
		leaseOwner,
		time.Now().UTC(),
		leaseUntil,
	)
}

func (store *Store) RenewScopeFanoutLease(ctx context.Context, outboxID string, leaseOwner string, leaseUntil time.Time) error {
	return message.NewScopeRepositories(store.db).Outbox.RenewLease(ctx, outboxID, leaseOwner, leaseUntil)
}

func (store *Store) ScopeFanoutAgentDelivered(
	ctx context.Context,
	sourceEventType string,
	sourceEventID string,
	agentID string,
) (bool, error) {
	return message.NewScopeRepositories(store.db).Outbox.HasAgentDeliveryReceipt(
		ctx,
		sourceEventType,
		sourceEventID,
		agentID,
	)
}

func (store *Store) RecordScopeFanoutAgentDelivered(
	ctx context.Context,
	outboxID string,
	leaseOwner string,
	sourceEventType string,
	sourceEventID string,
	agentID string,
) error {
	return message.NewScopeRepositories(store.db).Outbox.RecordAgentDeliveryReceipt(
		ctx,
		outboxID,
		leaseOwner,
		sourceEventType,
		sourceEventID,
		agentID,
		time.Now().UTC(),
	)
}

func (store *Store) CompleteScopeFanoutLease(ctx context.Context, outboxID string, leaseOwner string) error {
	return message.NewScopeRepositories(store.db).Outbox.MarkDelivered(
		ctx,
		outboxID,
		leaseOwner,
		time.Now().UTC(),
	)
}

func (store *Store) RetryScopeFanoutLease(
	ctx context.Context,
	outboxID string,
	leaseOwner string,
	nextAttemptAt time.Time,
	lastError string,
) error {
	return message.NewScopeRepositories(store.db).Outbox.MarkRetryOrFailed(
		ctx,
		outboxID,
		leaseOwner,
		nextAttemptAt,
		lastError,
	)
}

func (store *Store) DeferScopeFanoutLease(
	ctx context.Context,
	outboxID string,
	leaseOwner string,
	nextAttemptAt time.Time,
	lastError string,
) error {
	return message.NewScopeRepositories(store.db).Outbox.MarkDeferred(
		ctx,
		outboxID,
		leaseOwner,
		nextAttemptAt,
		lastError,
	)
}
