package automation_test

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/AgentTeam/internal/api/automation"
	"github.com/tidbcloud/AgentTeam/internal/config"
	"github.com/tidbcloud/AgentTeam/internal/identity/trusted"
	automationservice "github.com/tidbcloud/AgentTeam/internal/service/automation"
	automationstore "github.com/tidbcloud/AgentTeam/internal/store/automation"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func TestAutomationSecretRoutesRequireSecretPermissionsAndReturnMetadataOnly(t *testing.T) {
	mux, store := newAutomationSecretsAPITestMux(t)
	path := "/api/v1/automations/repositories/pingcap/agentteam/secrets/deploy_token"
	plaintext := "plain-secret-token"

	rec := automationSecretAPIRequest(t, mux, http.MethodPut, path, `{"value":"`+plaintext+`"}`, automationSecretJWT("automation:secret:read"))
	require.Equal(t, http.StatusForbidden, rec.Code, rec.Body.String())
	require.NotContains(t, rec.Body.String(), plaintext)

	rec = automationSecretAPIRequest(t, mux, http.MethodPut, path, `{"value":"`+plaintext+`"}`, automationSecretJWT("automation:secret:manage"))
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.Contains(t, rec.Body.String(), `"name":"deploy_token"`)
	require.Contains(t, rec.Body.String(), `"scope":"repository"`)
	require.NotContains(t, rec.Body.String(), plaintext)
	require.NotContains(t, rec.Body.String(), "ciphertext")
	require.NotContains(t, rec.Body.String(), "repositoryFullName")
	require.NotContains(t, rec.Body.String(), `"value"`)

	stored, err := store.GetAutomationSecret(t.Context(), "ws-1", "pingcap", "agentteam", "deploy_token")
	require.NoError(t, err)
	require.NotEmpty(t, stored.ValueCiphertext)
	require.NotEqual(t, plaintext, stored.ValueCiphertext)
	require.NotContains(t, stored.ValueCiphertext, plaintext)

	listPath := "/api/v1/automations/repositories/pingcap/agentteam/secrets"
	rec = automationSecretAPIRequest(t, mux, http.MethodGet, listPath, "", automationSecretJWT("automation:run:read"))
	require.Equal(t, http.StatusForbidden, rec.Code, rec.Body.String())

	rec = automationSecretAPIRequest(t, mux, http.MethodGet, listPath, "", automationSecretJWT("automation:secret:read"))
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.Contains(t, rec.Body.String(), `"name":"deploy_token"`)
	require.Contains(t, rec.Body.String(), `"hasMore":false`)
	require.NotContains(t, rec.Body.String(), plaintext)
	require.NotContains(t, rec.Body.String(), "ciphertext")
	require.NotContains(t, rec.Body.String(), "repositoryFullName")
	require.NotContains(t, rec.Body.String(), `"value"`)

	rec = automationSecretAPIRequest(t, mux, http.MethodPut, "/api/v1/automations/repositories/pingcap/agentteam/secrets/extra_token", `{"value":"extra-`+plaintext+`"}`, automationSecretJWT("automation:secret:manage"))
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())

	rec = automationSecretAPIRequest(t, mux, http.MethodGet, listPath+"?perPage=1", "", automationSecretJWT("automation:secret:read"))
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.Contains(t, rec.Body.String(), `"hasMore":true`)
	require.NotContains(t, rec.Body.String(), plaintext)
	require.NotContains(t, rec.Body.String(), "ciphertext")
	require.NotContains(t, rec.Body.String(), `"value"`)

	rec = automationSecretAPIRequest(t, mux, http.MethodDelete, path, "", automationSecretJWT("automation:secret:read"))
	require.Equal(t, http.StatusForbidden, rec.Code, rec.Body.String())

	rec = automationSecretAPIRequest(t, mux, http.MethodDelete, path, "", automationSecretJWT("automation:secret:manage"))
	require.Equal(t, http.StatusNoContent, rec.Code, rec.Body.String())
}

func newAutomationSecretsAPITestMux(t *testing.T) (http.Handler, *automationstore.Store) {
	t.Helper()
	db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(&automationstore.AutomationSecret{}))
	store := automationstore.New(db)
	now := func() time.Time { return time.Date(2026, 6, 14, 10, 30, 0, 0, time.UTC) }
	svc := automationservice.NewAutomationsService(store, now, nil)
	svc.SetAutomationCredentialsKey([]byte("0123456789abcdef0123456789abcdef"))
	svc.SetAuthorizer(automationservice.ClaimAutomationAuthorizer{})

	mux := http.NewServeMux()
	handler := automation.NewAutomationsHandler(svc, config.AutomationConfig{Roles: []string{config.AutomationRoleAPI}}, "Authorization")
	automation.RegisterRoutes(mux, handler, trusted.Middleware(trusted.Options{Enabled: true, Header: "Authorization"}))
	return mux, store
}

func automationSecretAPIRequest(t *testing.T, handler http.Handler, method string, path string, body string, token string) *httptest.ResponseRecorder {
	t.Helper()
	var payload *bytes.Buffer
	if body == "" {
		payload = bytes.NewBuffer(nil)
	} else {
		payload = bytes.NewBufferString(body)
	}
	req := httptest.NewRequest(method, path, payload)
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("X-Workspace-Id", "ws-1")
	rec := httptest.NewRecorder()
	handler.ServeHTTP(rec, req)
	return rec
}

func automationSecretJWT(permissions ...string) string {
	claims := map[string]any{
		"sub":                    "user-1",
		"workspace_ids":          []string{"ws-1"},
		"automation_permissions": permissions,
	}
	header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none"}`))
	payload, _ := json.Marshal(claims)
	return header + "." + base64.RawURLEncoding.EncodeToString(payload) + "."
}
