package store

import (
	"context"
	"testing"
	"time"
)

// TestDroppedDeliveryAttemptRoundTrips — Vikunja #370. A suppressed nudge is
// recorded as 'dropped'. The status column has a CHECK constraint, so this
// only works if migration #12 widened it; a fake outbox in a unit test would
// not catch that.
func TestDroppedDeliveryAttemptRoundTrips(t *testing.T) {
	s := newTestStore(t)
	ctx := context.Background()
	now := time.Now()

	id, err := s.BeginDeliveryAttempt(ctx, "nudge", "water", 0, "", "drop", "abc123", now)
	if err != nil {
		t.Fatalf("BeginDeliveryAttempt: %v", err)
	}
	if err := s.CompleteDeliveryAttempt(ctx, id, DeliveryDropped, now); err != nil {
		t.Fatalf("CompleteDeliveryAttempt: %v", err)
	}

	var status string
	err = s.db.QueryRowContext(ctx, `SELECT status FROM delivery_attempts WHERE id = ?`, id).Scan(&status)
	if err != nil {
		t.Fatalf("read back: %v", err)
	}
	if status != DeliveryDropped {
		t.Fatalf("status: want %q, got %q", DeliveryDropped, status)
	}
}

// TestListDeliveryAttempts — the read path the outbox lacked until #390. The
// two questions it must answer are "what was dropped" and "what is pending".
func TestListDeliveryAttempts(t *testing.T) {
	ctx := context.Background()
	s := newTestStore(t)
	base := time.Date(2026, 8, 4, 9, 0, 0, 0, time.UTC)

	sent, err := s.BeginDeliveryAttempt(ctx, "nudge", "water", 0, "", "telegram", "h1", base)
	if err != nil {
		t.Fatal(err)
	}
	if err := s.CompleteDeliveryAttempt(ctx, sent, DeliverySent, base.Add(time.Second)); err != nil {
		t.Fatal(err)
	}
	dropped, err := s.BeginDeliveryAttempt(ctx, "nudge", "care", 0, "", "telegram", "h2", base.Add(time.Minute))
	if err != nil {
		t.Fatal(err)
	}
	if err := s.CompleteDeliveryAttempt(ctx, dropped, DeliveryDropped, base.Add(time.Minute)); err != nil {
		t.Fatal(err)
	}
	reminderID, err := s.CreateReminder(ctx, base.Add(time.Hour), `{"text":"test"}`, "")
	if err != nil {
		t.Fatal(err)
	}
	reminders, err := s.ListReminders(ctx, 1)
	if err != nil || len(reminders) != 1 {
		t.Fatalf("list reminder: %+v, %v", reminders, err)
	}
	if err := s.CacheReminderPhrase(ctx, reminders, "reminder:test", "test", "test", "neutral"); err != nil {
		t.Fatal(err)
	}
	if _, err := s.BeginDeliveryAttempt(ctx, "reminder", "", reminderID, "reminder:test", "voice", "h3", base.Add(2*time.Minute)); err != nil {
		t.Fatal(err)
	}

	all, err := s.ListDeliveryAttempts(ctx, "", 10)
	if err != nil || len(all) != 3 {
		t.Fatalf("ListDeliveryAttempts = %d rows, err=%v, want 3", len(all), err)
	}
	// Newest first.
	if all[0].Kind != "reminder" || all[0].ReminderID != reminderID {
		t.Fatalf("newest row is %+v, want the reminder", all[0])
	}
	if all[0].HasComplete {
		t.Fatalf("a pending row must have no completion time: %+v", all[0])
	}
	if !all[2].HasComplete || !all[2].Completed.Equal(base.Add(time.Second)) {
		t.Fatalf("completed row lost its time: %+v", all[2])
	}

	only, err := s.ListDeliveryAttempts(ctx, DeliveryDropped, 10)
	if err != nil || len(only) != 1 || only[0].Rule != "care" {
		t.Fatalf("dropped filter = %+v, err=%v", only, err)
	}
	pending, err := s.ListDeliveryAttempts(ctx, DeliveryPending, 10)
	if err != nil || len(pending) != 1 || pending[0].Kind != "reminder" {
		t.Fatalf("pending filter = %+v, err=%v", pending, err)
	}
}
