package content

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

	store "github.com/tidbcloud/AgentTeam/internal/store/content"
)

type OutboxStore interface {
	LeaseDueOutbox(context.Context, time.Time, string, time.Time, int) ([]store.Outbox, error)
	MarkOutboxDelivered(context.Context, store.Outbox, string, time.Time) (bool, error)
	MarkOutboxAttemptFailed(context.Context, store.Outbox, string, error, time.Time) (bool, error)
}

type VersionCommittedEvent struct {
	ID, WorkspaceID, ContentID, VersionID string
}

type VersionCommittedConsumer interface {
	ConsumeVersionCommitted(context.Context, VersionCommittedEvent) error
}

type OutboxWorker struct {
	store         OutboxStore
	consumer      VersionCommittedConsumer
	leaseOwner    string
	leaseDuration time.Duration
	now           func() time.Time
}

func NewOutboxWorker(store OutboxStore, consumer VersionCommittedConsumer, leaseOwner string, leaseDuration time.Duration) (*OutboxWorker, error) {
	leaseOwner = strings.TrimSpace(leaseOwner)
	if store == nil || consumer == nil || leaseOwner == "" || leaseDuration <= 0 {
		return nil, fmt.Errorf("content outbox worker dependencies are required")
	}
	return &OutboxWorker{store: store, consumer: consumer, leaseOwner: leaseOwner, leaseDuration: leaseDuration, now: func() time.Time { return time.Now().UTC() }}, nil
}

type OutboxWorkerResult struct{ Leased, Delivered, Retried, Failed, Stale int }

func (worker *OutboxWorker) RunOnce(ctx context.Context, limit int) (OutboxWorkerResult, error) {
	if worker == nil {
		return OutboxWorkerResult{}, fmt.Errorf("content outbox worker is nil")
	}
	now := worker.now()
	rows, err := worker.store.LeaseDueOutbox(ctx, now, worker.leaseOwner, now.Add(worker.leaseDuration), limit)
	if err != nil {
		return OutboxWorkerResult{}, err
	}
	result := OutboxWorkerResult{Leased: len(rows)}
	for _, row := range rows {
		var consumeErr error
		if row.EventType == store.OutboxVersionCommitted {
			event := VersionCommittedEvent{ID: row.ID, WorkspaceID: row.WorkspaceID, ContentID: row.ContentID, VersionID: row.VersionID}
			consumeErr = worker.consumer.ConsumeVersionCommitted(ctx, event)
		} else {
			consumeErr = fmt.Errorf("unsupported content outbox event %q", row.EventType)
		}
		if consumeErr != nil {
			updated, markErr := worker.store.MarkOutboxAttemptFailed(ctx, row, worker.leaseOwner, consumeErr, worker.now())
			if markErr != nil {
				return result, markErr
			}
			if updated {
				if row.Attempts+1 >= store.MaxOutboxAttempts {
					result.Failed++
				} else {
					result.Retried++
				}
			} else {
				result.Stale++
			}
			continue
		}
		updated, err := worker.store.MarkOutboxDelivered(ctx, row, worker.leaseOwner, worker.now())
		if err != nil {
			return result, err
		}
		if updated {
			result.Delivered++
		} else {
			result.Stale++
		}
	}
	return result, nil
}
