package wire

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

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/upgrade"
	"github.com/tidbcloud/AgentTeam/desktop/internal/version"
)

// TestFetchUpgradeInfoNormalizesBareVersionPrefix simulates the full
// cross-boundary path: server returns a bare version "0.2.5" (because
// versioning.Provider strips the "v" prefix), desktop fetchUpgradeInfo
// normalizes it to "v0.2.5", and CheckVersion successfully fetches the
// correct manifest and does not fall back to CDN latest.
func TestFetchUpgradeInfoNormalizesBareVersionPrefix(t *testing.T) {
	upgrade.RegisterRecommendationRefresher(nil)
	defer upgrade.RegisterRecommendationRefresher(nil)

	oldVersion := version.Version
	version.Version = "v0.2.0"
	defer func() { version.Version = oldVersion }()

	// Server returns bare version (no "v" prefix),
	// matching what versioning.Provider produces after Normalize.
	upgradeInfoServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/api/v1/workspaces/wsp_1/daemon/upgrade-info" {
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"serverVersion":"1.0.0","recommendedDesktopVersion":"0.2.5","minDesktopVersion":"0.2.0"}`))
			return
		}
		http.NotFound(w, r)
	}))
	defer upgradeInfoServer.Close()

	// CDN: stable.json says latest is v0.9.0, but we also serve v0.2.5.json
	// because the pinned version manifest must be fetchable.
	mux := http.NewServeMux()
	cdnServer := httptest.NewServer(mux)
	defer cdnServer.Close()

	mux.HandleFunc("/stable.json", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, `{"schema_version":"1","product":"agentteam-daemon","channel":"stable","current_version":"v0.9.0","published_at":"2026-04-16T04:47:16Z","manifest_url":"%s/v0.9.0.json"}`, cdnServer.URL)
	})
	mux.HandleFunc("/v0.9.0.json", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, `{"schema_version":"1","product":"agentteam-daemon","version":"v0.9.0","published_at":"2026-04-16T04:47:16Z","manifest_url":"%s/v0.9.0.json","client":{"install_script":"https://static.pingcap.com/agentteam/client/install.sh","checksums":"https://static.pingcap.com/agentteam/client/v0.9.0/agentteam_0.9.0_checksums.txt"}}`, cdnServer.URL)
	})
	mux.HandleFunc("/v0.2.5.json", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, `{"schema_version":"1","product":"agentteam-daemon","version":"v0.2.5","published_at":"2026-04-16T04:47:16Z","manifest_url":"%s/v0.2.5.json","client":{"install_script":"https://static.pingcap.com/agentteam/client/install.sh","checksums":"https://static.pingcap.com/agentteam/client/v0.2.5/agentteam_0.2.5_checksums.txt"}}`, cdnServer.URL)
	})

	ch := &Protocol{
		id:          "agentteam-upgrade-test",
		serverURL:   upgradeInfoServer.URL,
		workspaceID: "wsp_1",
		apiKey:      "machine-key",
		httpClient:  &http.Client{Timeout: 10 * 1000 * 1000 * 1000},
	}

	// Register the channel's fetchUpgradeInfo as the refresher.
	upgrade.RegisterRecommendationRefresher(ch.fetchUpgradeInfo)
	defer upgrade.RegisterRecommendationRefresher(nil)

	key := upgrade.NormalizeServerKey(upgradeInfoServer.URL, "wsp_1")
	upgrade.SetActiveServer(key)
	defer upgrade.SetActiveServer("")

	// Trigger the fetch via EnsureServerRecommendation (simulates what
	// CheckVersion does when cache is empty).
	rec, err := upgrade.EnsureServerRecommendation(context.Background())
	require.NoError(t, err)
	require.NotNil(t, rec)
	// The recommendation should have "v" prefix normalized.
	require.Equal(t, "v0.2.5", rec.RecommendedDesktopVersion)
	require.Equal(t, "v0.2.0", rec.MinDesktopVersion)
	// ServerVersion is not used for manifest URL construction, so it stays as-is.
	require.Equal(t, "1.0.0", rec.ServerVersion)

	// Now run CheckVersion — it should use the cached recommendation (v0.2.5)
	// as the upgrade target, fetch /v0.2.5.json, and validate successfully.
	result, err := upgrade.CheckVersion(context.Background(), cdnServer.URL+"/stable.json")
	require.NoError(t, err)
	require.Equal(t, "v0.2.5", result.Latest, "should use server-recommended version, not CDN latest v0.9.0")
	require.True(t, result.HasUpdate)
	require.Contains(t, result.ManifestURL, "/v0.2.5.json",
		"manifest URL should point to v0.2.5.json, not 0.2.5.json or v0.9.0.json")
}

// TestEnsureVersionPrefix covers the boundary normalization helper.
func TestEnsureVersionPrefix(t *testing.T) {
	require.Equal(t, "v0.2.5", ensureVersionPrefix("0.2.5"))
	require.Equal(t, "v0.2.5", ensureVersionPrefix("v0.2.5"))
	require.Equal(t, "V0.2.5", ensureVersionPrefix("V0.2.5"))
	require.Equal(t, "", ensureVersionPrefix(""))
}

// TestFetchUpgradeInfoHandles404 verifies that an older server without the
// endpoint returns nil (no error), leaving the cache empty for CDN fallback.
func TestFetchUpgradeInfoHandles404(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
	}))
	defer server.Close()

	ch := &Protocol{
		id:          "agentteam-404-test",
		serverURL:   server.URL,
		workspaceID: "wsp_1",
		apiKey:      "machine-key",
		httpClient:  &http.Client{Timeout: 10 * 1000 * 1000 * 1000},
	}

	rec, err := ch.fetchUpgradeInfo(context.Background(), string(upgrade.NormalizeServerKey(server.URL, "wsp_1")))
	require.NoError(t, err)
	require.Nil(t, rec)
}

// TestFetchUpgradeInfoParsesResponse verifies the JSON payload is correctly
// parsed and version-prefixed.
func TestFetchUpgradeInfoParsesResponse(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, "/api/v1/workspaces/wsp_1/daemon/upgrade-info", r.URL.Path)
		require.Equal(t, "Bearer machine-key", r.Header.Get("Authorization"))
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"serverVersion":"2.0.0","recommendedDesktopVersion":"1.1.3","minDesktopVersion":"1.1.2"}`))
	}))
	defer server.Close()

	ch := &Protocol{
		id:          "agentteam-parse-test",
		serverURL:   server.URL,
		workspaceID: "wsp_1",
		apiKey:      "machine-key",
		httpClient:  &http.Client{Timeout: 10 * 1000 * 1000 * 1000},
	}

	rec, err := ch.fetchUpgradeInfo(context.Background(), string(upgrade.NormalizeServerKey(server.URL, "wsp_1")))
	require.NoError(t, err)
	require.NotNil(t, rec)
	require.Equal(t, "2.0.0", rec.ServerVersion)
	require.Equal(t, "v1.1.3", rec.RecommendedDesktopVersion)
	require.Equal(t, "v1.1.2", rec.MinDesktopVersion)
}

// TestFetchUpgradeInfoIgnoresWrongServerKey verifies that a fetch callback
// for a different server key returns nil without making an HTTP request.
func TestFetchUpgradeInfoIgnoresWrongServerKey(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		t.Fatal("should not make HTTP request for wrong server key")
	}))
	defer server.Close()

	ch := &Protocol{
		id:         "agentteam-wrong-key-test",
		serverURL:  server.URL,
		apiKey:     "machine-key",
		httpClient: &http.Client{Timeout: 10 * 1000 * 1000 * 1000},
	}

	wrongKey := string(upgrade.NormalizeServerKey("https://other-server", "wsp_other"))
	rec, err := ch.fetchUpgradeInfo(context.Background(), wrongKey)
	require.NoError(t, err)
	require.Nil(t, rec)
}
