package app

import (
	"context"
	"errors"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/testkit/httputil"
	"github.com/tidbcloud/AgentTeam/internal/extension/providers/searchengine/runtime"
	storeapp "github.com/tidbcloud/AgentTeam/internal/store/app"
)

type recordingSearchEngineTester struct {
	connection runtime.RuntimeConnection
	err        error
}

func (tester *recordingSearchEngineTester) TestConnection(
	_ context.Context,
	connection runtime.RuntimeConnection,
) error {
	tester.connection = connection
	return tester.err
}

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

	updatedAt := time.Date(2026, 8, 15, 1, 0, 0, 0, time.UTC)
	fake := newFakeStore()
	fake.searchEngineConnection = storeapp.SearchEngineConnectionView{
		Provider:         runtime.ProviderBrave,
		Active:           true,
		APIKeyConfigured: true,
		UpdatedAt:        &updatedAt,
	}
	fake.searchEngineRuntime = runtime.RuntimeConnection{
		Provider: runtime.ProviderBrave,
		APIKey:   "server-only-secret",
	}
	tester := &recordingSearchEngineTester{}
	router := newAppRouter(Dependencies{
		AppStore:           fake,
		SearchEngineTester: tester,
	})
	path := "/api/v1/workspaces/wsp_1/app-installations/appinst_brave/search-engine-connection"

	resp := httputil.DoJSON(t, router, http.MethodGet, path, nil)
	require.Equal(t, http.StatusOK, resp.Code)
	require.Equal(t, "no-store", resp.Header().Get("Cache-Control"))
	require.JSONEq(t, `{
		"provider":"brave",
		"active":true,
		"config":{},
		"credentials":{"apiKeyConfigured":true},
		"updatedAt":"2026-08-15T01:00:00Z"
	}`, resp.Body.String())
	require.NotContains(t, resp.Body.String(), "server-only-secret")

	resp = httputil.DoJSON(t, router, http.MethodPut, path, map[string]any{
		"config": map[string]any{},
		"credentials": map[string]any{
			"apiKey": "replacement-secret",
		},
	})
	require.Equal(t, http.StatusOK, resp.Code)
	require.Equal(t, "put", fake.lastSearchOperation)
	require.Equal(t, "replacement-secret", fake.lastSearchParams.Credentials["apiKey"])
	require.NotContains(t, resp.Body.String(), "replacement-secret")

	resp = httputil.DoJSON(t, router, http.MethodPost, path+":test", nil)
	require.Equal(t, http.StatusOK, resp.Code)
	require.JSONEq(t, `{"ok":true}`, resp.Body.String())
	require.Equal(t, "server-only-secret", tester.connection.APIKey)
	require.NotContains(t, resp.Body.String(), "server-only-secret")

	resp = httputil.DoJSON(t, router, http.MethodDelete, path, nil)
	require.Equal(t, http.StatusOK, resp.Code)
	require.Equal(t, "delete", fake.lastSearchOperation)
}

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

	fake := newFakeStore()
	fake.searchEngineConnection = storeapp.SearchEngineConnectionView{
		Provider: runtime.ProviderBrave,
		Active:   true,
	}
	fake.searchEngineRuntime = runtime.RuntimeConnection{
		Provider: runtime.ProviderBrave,
		APIKey:   "secret",
	}
	tester := &recordingSearchEngineTester{err: &runtime.Error{
		Code:    runtime.ErrorUpstreamFailure,
		Message: "secret upstream detail",
		Cause:   errors.New("secret transport detail"),
	}}
	router := newAppRouter(Dependencies{
		AppStore:           fake,
		SearchEngineTester: tester,
	})

	resp := httputil.DoJSON(
		t,
		router,
		http.MethodPost,
		"/api/v1/workspaces/wsp_1/app-installations/appinst_brave/search-engine-connection:test",
		nil,
	)
	require.Equal(t, http.StatusBadGateway, resp.Code)
	require.NotContains(t, resp.Body.String(), "secret")
}

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

	fake := newFakeStore()
	router := newAppRouter(Dependencies{AppStore: fake})
	path := "/api/v1/workspaces/wsp_1/app-installations/appinst_brave/search-engine-connection"
	body := `{"config":{},"credentials":{"apiKey":"` +
		strings.Repeat("x", 64*1024) +
		`"}}`

	response := httputil.DoRaw(
		t,
		router,
		http.MethodPut,
		path,
		http.Header{
			"Authorization":  {"Bearer token"},
			"Content-Type":   {"application/json"},
			"X-Workspace-Id": {"wsp_1"},
		},
		[]byte(body),
	)
	require.Equal(t, http.StatusRequestEntityTooLarge, response.Code)
	require.Empty(t, fake.lastSearchOperation)
}
