package storage

import (
	"errors"
	"testing"
)

func TestRecordPollResult_SuccessThenFailure(t *testing.T) {
	setupTestDB(t)

	// First a successful poll returning 7 items.
	RecordPollResult("Feed A", true, 7, nil)
	h, err := ListSourceHealth()
	if err != nil {
		t.Fatal(err)
	}
	a, ok := h["Feed A"]
	if !ok {
		t.Fatal("expected a health row for Feed A")
	}
	if a.LastItemCount != 7 || a.ConsecutiveFailures != 0 || a.LastError != "" {
		t.Errorf("after success: items=%d fails=%d err=%q", a.LastItemCount, a.ConsecutiveFailures, a.LastError)
	}
	if a.LastPollAt == 0 || a.LastSuccessAt == 0 {
		t.Errorf("after success: last_poll=%d last_success=%d, want both set", a.LastPollAt, a.LastSuccessAt)
	}
	successTS := a.LastSuccessAt

	// Two failures in a row: failures accumulate, but the last-success timestamp
	// and item count are preserved so the dashboard still shows when it last worked.
	RecordPollResult("Feed A", false, 0, errors.New("dial tcp: timeout"))
	RecordPollResult("Feed A", false, 0, errors.New("502 bad gateway"))
	h, _ = ListSourceHealth()
	a = h["Feed A"]
	if a.ConsecutiveFailures != 2 {
		t.Errorf("consecutive failures = %d, want 2", a.ConsecutiveFailures)
	}
	if a.LastError != "502 bad gateway" {
		t.Errorf("last error = %q, want %q", a.LastError, "502 bad gateway")
	}
	if a.LastSuccessAt != successTS {
		t.Errorf("last success = %d, want preserved %d", a.LastSuccessAt, successTS)
	}
	if a.LastItemCount != 7 {
		t.Errorf("last item count = %d, want preserved 7", a.LastItemCount)
	}

	// Recovery clears the error and resets the counter.
	RecordPollResult("Feed A", true, 3, nil)
	h, _ = ListSourceHealth()
	a = h["Feed A"]
	if a.ConsecutiveFailures != 0 || a.LastError != "" || a.LastItemCount != 3 {
		t.Errorf("after recovery: fails=%d err=%q items=%d", a.ConsecutiveFailures, a.LastError, a.LastItemCount)
	}
}

func TestSourceContentStats(t *testing.T) {
	setupTestDB(t)

	// Source X: two classified stories, one paywalled; one posted.
	InsertStory(&Story{GUID: "x1", Headline: "X one", ArticleURL: "https://x.com/1", Source: "X", Channel: "tech", Classified: true, SeenAt: 100})
	InsertStory(&Story{GUID: "x2", Headline: "X two", ArticleURL: "https://x.com/2", Source: "X", Channel: "tech", Classified: true, Paywalled: true, SeenAt: 200})
	// Source Y: one unclassified story, never posted.
	InsertStory(&Story{GUID: "y1", Headline: "Y one", ArticleURL: "https://y.com/1", Source: "Y", SeenAt: 150})

	InsertPostLog("x1", "tech", "$e1", "", false)
	// Backdate/forward the post so we can assert MAX(posted_at).
	Get().Exec(`UPDATE post_log SET posted_at = ? WHERE guid = ?`, int64(500), "x1")

	stats, err := SourceContentStats()
	if err != nil {
		t.Fatal(err)
	}

	x := stats["X"]
	if x.Total != 2 || x.Classified != 2 || x.Paywalled != 1 {
		t.Errorf("X: total=%d classified=%d paywalled=%d, want 2/2/1", x.Total, x.Classified, x.Paywalled)
	}
	if x.LastSeenAt != 200 {
		t.Errorf("X last seen = %d, want 200", x.LastSeenAt)
	}
	if x.LastPostedAt != 500 {
		t.Errorf("X last posted = %d, want 500", x.LastPostedAt)
	}

	y := stats["Y"]
	if y.Total != 1 || y.Classified != 0 || y.Paywalled != 0 {
		t.Errorf("Y: total=%d classified=%d paywalled=%d, want 1/0/0", y.Total, y.Classified, y.Paywalled)
	}
	if y.LastPostedAt != 0 {
		t.Errorf("Y last posted = %d, want 0 (never posted)", y.LastPostedAt)
	}
}

func TestListSourceHealth_Empty(t *testing.T) {
	setupTestDB(t)
	h, err := ListSourceHealth()
	if err != nil {
		t.Fatal(err)
	}
	if len(h) != 0 {
		t.Errorf("expected no health rows, got %d", len(h))
	}
}
