package provider

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/testkit/httputil"
	"github.com/tidbcloud/AgentTeam/internal/store/provider"
)

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

	fake := newFakeStore()
	fake.githubLoadConfig = provider.GitHubAppConfig{AppSlug: "agentteam-http", AppID: 4242}
	fake.githubLoadFound = true
	router := newProviderRouter(newGitHubTestDeps(t, fake, nil, ""))

	response := httputil.DoJSON(t, router, http.MethodGet, "/api/v1/workspaces/wsp_1/providers/github/installation", nil)
	require.Equal(t, http.StatusOK, response.Code)
	require.Equal(t, "no-store", response.Header().Get("Cache-Control"))
}

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

	mux := http.NewServeMux()
	mux.HandleFunc("/app-manifests/test-code/conversions", func(response http.ResponseWriter, request *http.Request) {
		require.Equal(t, http.MethodPost, request.Method)
		require.NoError(t, json.NewEncoder(response).Encode(map[string]any{
			"id":             4242,
			"slug":           "agentteam-http",
			"name":           "AgentTeam HTTP",
			"client_id":      "Iv1.http",
			"client_secret":  "client-secret",
			"pem":            "private-key",
			"webhook_secret": "webhook-secret",
			"html_url":       "https://github.example.com/apps/agentteam-http",
			"owner":          map[string]any{"login": "acme", "id": 7},
		}))
	})
	github := httptest.NewServer(mux)
	defer github.Close()

	router := newProviderRouter(newGitHubTestDeps(t, newFakeStore(), nil, github.URL))
	started := httputil.DoJSON(t, router, http.MethodGet, "/api/v1/workspaces/wsp_1/providers/github/registration", nil)
	require.Equal(t, http.StatusOK, started.Code)
	var body struct {
		FormAction string `json:"formAction"`
	}
	require.NoError(t, json.Unmarshal(started.Body.Bytes(), &body))
	formAction, err := url.Parse(body.FormAction)
	require.NoError(t, err)
	state := formAction.Query().Get("state")
	require.NotEmpty(t, state)

	response := httputil.DoRaw(t, router, http.MethodGet, "/api/github/callback?code=test-code&state="+url.QueryEscape(state), http.Header{
		"Cookie": {started.Header().Get("Set-Cookie")},
	}, nil)
	require.Equal(t, http.StatusFound, response.Code)
	require.Contains(t, response.Header().Get("Location"), "registered")
	require.Equal(t, "no-store", response.Header().Get("Cache-Control"))
}

func TestGitHubSetupCallbackRedirectsAreNoStore(t *testing.T) {
	t.Run("requested", func(t *testing.T) {
		router := newProviderRouter(newGitHubTestDeps(t, newFakeStore(), nil, ""))

		response := httputil.DoRaw(t, router, http.MethodGet, "/api/github/setup?state=unused&setup_action=request", nil, nil)
		require.Equal(t, http.StatusFound, response.Code)
		require.Contains(t, response.Header().Get("Location"), "requested")
		require.Equal(t, "no-store", response.Header().Get("Cache-Control"))
	})

	t.Run("approved", func(t *testing.T) {
		router := newProviderRouter(newGitHubTestDeps(t, newFakeStore(), nil, ""))

		response := httputil.DoRaw(t, router, http.MethodGet, "/api/github/setup?installation_id=789&state=unused&setup_action=install", nil, nil)
		require.Equal(t, http.StatusFound, response.Code)
		require.Contains(t, response.Header().Get("Location"), "approved")
		require.Equal(t, "no-store", response.Header().Get("Cache-Control"))
	})

	t.Run("installed", func(t *testing.T) {
		github := newGitHubMockServer(t)
		defer github.Close()

		fake := newFakeStore()
		fake.githubLoadConfig = provider.GitHubAppConfig{
			AppID:         4242,
			AppSlug:       "agentteam-http",
			PrivateKeyPEM: generateTestPEM(t),
		}
		fake.githubLoadFound = true
		router := newProviderRouter(newGitHubTestDeps(t, fake, nil, github.URL))

		started := httputil.DoJSON(t, router, http.MethodGet, "/api/v1/workspaces/wsp_1/providers/github/installation", nil)
		require.Equal(t, http.StatusOK, started.Code)
		var body struct {
			InstallURL string `json:"installUrl"`
		}
		require.NoError(t, json.Unmarshal(started.Body.Bytes(), &body))
		installURL, err := url.Parse(body.InstallURL)
		require.NoError(t, err)
		state := installURL.Query().Get("state")
		require.NotEmpty(t, state)

		response := httputil.DoRaw(t, router, http.MethodGet, "/api/github/setup?installation_id=789&setup_action=install&state="+url.QueryEscape(state), http.Header{
			"Cookie": {started.Header().Get("Set-Cookie")},
		}, nil)
		require.Equal(t, http.StatusFound, response.Code)
		require.Contains(t, response.Header().Get("Location"), "installed")
		require.Equal(t, "no-store", response.Header().Get("Cache-Control"))
	})
}
