package versioning

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestStableManifestProviderReturnsNormalizedCurrentVersion(t *testing.T) {
	t.Parallel()

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, "/stable.json", r.URL.Path)
		fmt.Fprint(w, `{"schema_version":"1","product":"agentteam-desktop","channel":"stable","current_version":"v1.2.3","published_at":"2026-05-21T00:00:00Z","manifest_url":"https://example.test/v1.2.3.json"}`)
	}))
	t.Cleanup(server.Close)

	provider := NewStableManifestProvider(StableManifestProviderConfig{
		StableURL:   server.URL + "/stable.json",
		FallbackURL: server.URL + "/fallback.json",
		CacheTTL:    time.Hour,
		HTTPClient:  server.Client(),
	})

	got, err := provider.Latest(context.Background())
	require.NoError(t, err)
	require.Equal(t, "1.2.3", got)
}

func TestStableManifestProviderFallsBackToDaemonManifest(t *testing.T) {
	t.Parallel()

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/desktop/stable.json":
			http.Error(w, "missing", http.StatusNotFound)
		case "/daemon/stable.json":
			fmt.Fprint(w, `{"schema_version":"1","product":"agentteam-daemon","channel":"stable","current_version":"v1.2.4","published_at":"2026-05-21T00:00:00Z","manifest_url":"https://example.test/v1.2.4.json"}`)
		default:
			http.NotFound(w, r)
		}
	}))
	t.Cleanup(server.Close)

	provider := NewStableManifestProvider(StableManifestProviderConfig{
		StableURL:   server.URL + "/desktop/stable.json",
		FallbackURL: server.URL + "/daemon/stable.json",
		CacheTTL:    time.Hour,
		HTTPClient:  server.Client(),
	})

	got, err := provider.Latest(context.Background())
	require.NoError(t, err)
	require.Equal(t, "1.2.4", got)
}

func TestStableManifestProviderSilentlyReturnsEmptyWhenInitialFetchFails(t *testing.T) {
	t.Parallel()

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "unavailable", http.StatusServiceUnavailable)
	}))
	t.Cleanup(server.Close)

	provider := NewStableManifestProvider(StableManifestProviderConfig{
		StableURL:   server.URL + "/stable.json",
		FallbackURL: server.URL + "/fallback.json",
		CacheTTL:    time.Hour,
		RetryTTL:    time.Minute,
		HTTPClient:  server.Client(),
	})

	got, err := provider.Latest(context.Background())
	require.NoError(t, err)
	require.Empty(t, got)
}

func TestStableManifestProviderReturnsStaleVersionWhenRefreshFails(t *testing.T) {
	t.Parallel()

	now := time.Date(2026, 5, 21, 12, 0, 0, 0, time.UTC)
	ok := true
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !ok {
			http.Error(w, "unavailable", http.StatusServiceUnavailable)
			return
		}
		fmt.Fprint(w, `{"schema_version":"1","product":"agentteam-desktop","channel":"stable","current_version":"v1.2.5","published_at":"2026-05-21T00:00:00Z","manifest_url":"https://example.test/v1.2.5.json"}`)
	}))
	t.Cleanup(server.Close)

	provider := NewStableManifestProvider(StableManifestProviderConfig{
		StableURL:   server.URL + "/stable.json",
		FallbackURL: server.URL + "/fallback.json",
		CacheTTL:    time.Hour,
		RetryTTL:    time.Minute,
		HTTPClient:  server.Client(),
		Now:         func() time.Time { return now },
	})

	got, err := provider.Latest(context.Background())
	require.NoError(t, err)
	require.Equal(t, "1.2.5", got)

	ok = false
	now = now.Add(2 * time.Hour)
	got, err = provider.Latest(context.Background())
	require.NoError(t, err)
	require.Equal(t, "1.2.5", got)
}

func TestStableManifestProviderCachesNegativeInitialResult(t *testing.T) {
	t.Parallel()

	now := time.Date(2026, 5, 21, 12, 0, 0, 0, time.UTC)
	requests := 0
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		requests++
		http.Error(w, "unavailable", http.StatusServiceUnavailable)
	}))
	t.Cleanup(server.Close)

	provider := NewStableManifestProvider(StableManifestProviderConfig{
		StableURL:   server.URL + "/stable.json",
		FallbackURL: server.URL + "/fallback.json",
		CacheTTL:    time.Hour,
		RetryTTL:    time.Minute,
		HTTPClient:  server.Client(),
		Now:         func() time.Time { return now },
	})

	got, err := provider.Latest(context.Background())
	require.NoError(t, err)
	require.Empty(t, got)
	require.Equal(t, 2, requests)

	got, err = provider.Latest(context.Background())
	require.NoError(t, err)
	require.Empty(t, got)
	require.Equal(t, 2, requests)
}

func TestStableManifestCurrentVersionRejectsUnexpectedProduct(t *testing.T) {
	t.Parallel()

	_, err := stableManifestCurrentVersion(stableManifest{
		Product: "agentteam-server",
		Current: "v1.2.3",
	})
	require.Error(t, err)
}
