package serverruntime

import (
	"context"
	"database/sql"
	"errors"
	"testing"
	"time"

	connector_events "github.com/pingcap-inc/nexuscrm-connectors/features/connector/events"
)

func TestConnectorQueueObservationRetainsLastGoodSnapshotOnFailure(t *testing.T) {
	collectedAt := time.Date(2026, 8, 16, 12, 0, 0, 0, time.UTC)
	wantStats := connector_events.QueueStats{
		CollectedAt: collectedAt,
		Global: connector_events.QueueBacklogStats{
			Pending: 7,
			Due:     3,
		},
		ByQueueClass: []connector_events.QueueClassStats{{
			Namespace:       "neutral",
			InstalledPackID: "pack-1",
			QueueClass:      "default",
			Stats:           connector_events.QueueBacklogStats{Pending: 7, Due: 3},
		}},
	}
	calls := 0
	observation := newConnectorQueueObservation()
	observation.query = func(context.Context, *sql.DB) (connector_events.QueueStats, error) {
		calls++
		if calls == 1 {
			return wantStats, nil
		}
		return connector_events.QueueStats{}, errors.New("observation timeout")
	}

	if err := observation.refresh(t.Context(), nil); err != nil {
		t.Fatalf("first refresh: %v", err)
	}
	firstStats, firstAvailable, firstStale, firstLastSuccess := observation.ConnectorQueueSnapshot()
	if !firstAvailable || firstStale || firstLastSuccess != collectedAt {
		t.Fatalf(
			"first snapshot status available=%v stale=%v last_success=%s, want available fresh at %s",
			firstAvailable,
			firstStale,
			firstLastSuccess,
			collectedAt,
		)
	}
	if firstStats.Global.Pending != 7 || len(firstStats.ByQueueClass) != 1 {
		t.Fatalf("first snapshot stats = %+v, want retained queue data", firstStats)
	}

	if err := observation.refresh(t.Context(), nil); err == nil {
		t.Fatal("second refresh succeeded, want error")
	}
	afterFailureStats, afterFailureAvailable, afterFailureStale, afterFailureLastSuccess := observation.ConnectorQueueSnapshot()
	if !afterFailureAvailable || !afterFailureStale || afterFailureLastSuccess != collectedAt {
		t.Fatalf(
			"failed snapshot status available=%v stale=%v last_success=%s, want available stale last-good",
			afterFailureAvailable,
			afterFailureStale,
			afterFailureLastSuccess,
		)
	}
	if afterFailureStats.Global.Pending != 7 || len(afterFailureStats.ByQueueClass) != 1 {
		t.Fatalf("failed snapshot lost last-good data: %+v", afterFailureStats)
	}
}

func TestConnectorQueueObservationReturnsImmutableSnapshotCopies(t *testing.T) {
	observation := newConnectorQueueObservation()
	observation.query = func(context.Context, *sql.DB) (connector_events.QueueStats, error) {
		return connector_events.QueueStats{
			CollectedAt: time.Now().UTC(),
			ByQueueClass: []connector_events.QueueClassStats{{
				Namespace: "original",
			}},
		}, nil
	}
	if err := observation.refresh(t.Context(), nil); err != nil {
		t.Fatalf("refresh: %v", err)
	}

	first, _, _, _ := observation.ConnectorQueueSnapshot()
	first.ByQueueClass[0].Namespace = "mutated"
	second, _, _, _ := observation.ConnectorQueueSnapshot()
	if got := second.ByQueueClass[0].Namespace; got != "original" {
		t.Fatalf("cached queue class namespace = %q after consumer mutation, want original", got)
	}
}
