package outbox

import (
	"context"
	"path/filepath"
	"testing"
)

// openTemp opens a fresh outbox in a temp dir.
func openTemp(t *testing.T) *Store {
	t.Helper()
	s, err := Open(filepath.Join(t.TempDir(), "stars.db"))
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	t.Cleanup(func() { _ = s.Close() })
	return s
}

func TestOutboxAddPendingMark(t *testing.T) {
	ctx := context.Background()
	s := openTemp(t)

	rec := Record{ChargeID: "ch1", OrderID: "ord1", Amount: 40, UserID: 777}
	if err := s.Add(ctx, rec); err != nil {
		t.Fatalf("add: %v", err)
	}
	pending, err := s.Pending(ctx, 10)
	if err != nil {
		t.Fatalf("pending: %v", err)
	}
	if len(pending) != 1 || pending[0] != rec {
		t.Fatalf("pending = %+v, want [%+v]", pending, rec)
	}

	if err := s.MarkForwarded(ctx, "ch1"); err != nil {
		t.Fatalf("mark: %v", err)
	}
	pending, err = s.Pending(ctx, 10)
	if err != nil {
		t.Fatalf("pending after mark: %v", err)
	}
	if len(pending) != 0 {
		t.Fatalf("pending after mark = %+v, want empty", pending)
	}
}

func TestOutboxAddIdempotent(t *testing.T) {
	ctx := context.Background()
	s := openTemp(t)

	rec := Record{ChargeID: "dup", OrderID: "ord1", Amount: 80, UserID: 1}
	if err := s.Add(ctx, rec); err != nil {
		t.Fatalf("add 1: %v", err)
	}
	// A re-delivered payment (same charge id) must not add a second row, even with different fields.
	if err := s.Add(ctx, Record{ChargeID: "dup", OrderID: "other", Amount: 999, UserID: 2}); err != nil {
		t.Fatalf("add 2: %v", err)
	}
	pending, err := s.Pending(ctx, 10)
	if err != nil {
		t.Fatalf("pending: %v", err)
	}
	if len(pending) != 1 || pending[0] != rec {
		t.Fatalf("pending = %+v, want the single original row", pending)
	}
}

func TestOutboxAddIgnoresForwarded(t *testing.T) {
	ctx := context.Background()
	s := openTemp(t)

	rec := Record{ChargeID: "ch", OrderID: "o", Amount: 40, UserID: 5}
	if err := s.Add(ctx, rec); err != nil {
		t.Fatalf("add: %v", err)
	}
	if err := s.MarkForwarded(ctx, "ch"); err != nil {
		t.Fatalf("mark: %v", err)
	}
	// A re-delivery after forwarding must not resurrect the row as pending (no double credit).
	if err := s.Add(ctx, rec); err != nil {
		t.Fatalf("re-add: %v", err)
	}
	pending, err := s.Pending(ctx, 10)
	if err != nil {
		t.Fatalf("pending: %v", err)
	}
	if len(pending) != 0 {
		t.Fatalf("pending = %+v, want empty (already forwarded)", pending)
	}
}

// TestOutboxReopenReDrives proves a restart re-drives undelivered payments: a payment persisted but
// not marked forwarded is still pending after the store is reopened from the same file.
func TestOutboxReopenReDrives(t *testing.T) {
	ctx := context.Background()
	dir := t.TempDir()
	path := filepath.Join(dir, "stars.db")

	s, err := Open(path)
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	rec := Record{ChargeID: "persist", OrderID: "o1", Amount: 40, UserID: 9}
	if err := s.Add(ctx, rec); err != nil {
		t.Fatalf("add: %v", err)
	}
	if err := s.Close(); err != nil {
		t.Fatalf("close: %v", err)
	}

	reopened, err := Open(path)
	if err != nil {
		t.Fatalf("reopen: %v", err)
	}
	defer func() { _ = reopened.Close() }()
	pending, err := reopened.Pending(ctx, 10)
	if err != nil {
		t.Fatalf("pending: %v", err)
	}
	if len(pending) != 1 || pending[0] != rec {
		t.Fatalf("pending after reopen = %+v, want the undelivered payment", pending)
	}
}
