package server

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

	agsconfig "github.com/pingcap/agent-git-service/config"
	agsserver "github.com/pingcap/agent-git-service/server"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/config"
	"github.com/tidbcloud/AgentTeam/internal/service/automation"
)

func TestAutomationRolesGateHTTPEndpoints(t *testing.T) {
	auth := "Bearer " + jwt(map[string]any{"sub": "role-user"})
	workspaceID := "ws-automation-roles"

	t.Run("api role exposes control-plane APIs but not scheduler runner dispatch or eventer", func(t *testing.T) {
		srv := newAutomationRoleTestServer(t, []string{config.AutomationRoleAPI})

		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodGet, "/api/v1/automations/runs", nil, http.StatusOK)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/runs", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/events/push", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runners/register", bytes.NewBufferString(`{"runnerId":"runner-1"}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v3/actions/runner-registration", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodGet, "/_broker/message", nil, http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/_runs/acquirejob", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runs/wfr_missing/waits/wfw_missing/resume", bytes.NewBufferString(`{"resultJson":"{}"}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/waits:deliver-event", bytes.NewBufferString(`{"waitKind":"wait_event","eventType":"provider.ready","resourceType":"provider","resourceId":"installation-1"}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runs/wfr_missing/waits/wfw_missing/approve", bytes.NewBufferString(`{"approved":true}`), http.StatusNotFound)
	})

	t.Run("scheduler role exposes run lifecycle writes without broad api reads", func(t *testing.T) {
		srv := newAutomationRoleTestServer(t, []string{config.AutomationRoleScheduler})

		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodGet, "/api/v1/automations/runs", nil, http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/runs", bytes.NewBufferString(`{}`), http.StatusBadRequest)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/events/push", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runs/wfr_missing/cancel", bytes.NewBufferString(`{"reason":"role test"}`), http.StatusNotFound)
	})

	t.Run("runner dispatch role exposes runner callbacks only", func(t *testing.T) {
		srv := newAutomationRoleTestServer(t, []string{config.AutomationRoleRunnerDispatch})

		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodGet, "/api/v1/automations/runs", nil, http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runners/register", bytes.NewBufferString(`{"runnerId":"runner-1"}`), http.StatusCreated)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v3/actions/runner-registration", bytes.NewBufferString(`{}`), http.StatusUnauthorized)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodGet, "/_broker/message", nil, http.StatusUnauthorized)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/_runs/acquirejob", bytes.NewBufferString(`{}`), http.StatusUnauthorized)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/runs", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/events/push", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
	})

	t.Run("eventer role exposes event ingress and wait resume without api reads", func(t *testing.T) {
		srv := newAutomationRoleTestServer(t, []string{config.AutomationRoleEventer})

		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodGet, "/api/v1/automations/runs", nil, http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/events/push", bytes.NewBufferString(`{}`), http.StatusBadRequest)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runs/wfr_missing/waits/wfw_missing/resume", bytes.NewBufferString(`{"resultJson":"{}"}`), http.StatusNotFound)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/waits:deliver-event", bytes.NewBufferString(`{"waitKind":"wait_event","eventType":"provider.ready","resourceType":"provider","resourceId":"installation-1"}`), http.StatusNotFound)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runs/wfr_missing/waits/wfw_missing/approve", bytes.NewBufferString(`{"approved":true}`), http.StatusNotFound)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runs/wfr_missing/waits/wfw_missing/complete-task", bytes.NewBufferString(`{"taskId":"tsk_1"}`), http.StatusNotFound)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runs/wfr_missing/waits/wfw_missing/signal-event", bytes.NewBufferString(`{"eventType":"automation_event_received"}`), http.StatusNotFound)
	})

	t.Run("reconciler-only role leaves automation HTTP target unavailable", func(t *testing.T) {
		srv := newAutomationRoleTestServer(t, []string{config.AutomationRoleReconciler})

		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodGet, "/api/v1/automations/runs", nil, http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v1/automations/runners/register", bytes.NewBufferString(`{"runnerId":"runner-1"}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/api/v3/actions/runner-registration", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodGet, "/_broker/message", nil, http.StatusServiceUnavailable)
		assertAutomationRoleRequestStatus(t, srv, auth, workspaceID, http.MethodPost, "/_runs/acquirejob", bytes.NewBufferString(`{}`), http.StatusServiceUnavailable)
	})
}

func newAutomationRoleTestServer(t *testing.T, roles []string) *Server {
	t.Helper()
	srv, err := New(config.ResourceRuntimeConfig{
		HTTPAddress: ":0",
		Auth:        config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"},
		Database:    config.ResourceDatabaseConfig{DSN: meshxDatabaseDSN(t)},
		AGS:         config.AGSConfig{DatabaseDSN: prepareResolverDB(t), GitRepoDir: t.TempDir()},
		Automation:  config.AutomationConfig{Roles: roles},
	}, Options{AutomationAuthorizer: automation.AllowAutomationAuthorizer{}, NewAGS: func(agsconfig.Config, ...agsserver.Option) (agsServer, error) {
		return fakeAGS{handler: http.NotFoundHandler()}, nil
	}})
	require.NoError(t, err)
	t.Cleanup(func() { require.NoError(t, srv.Shutdown(context.Background())) })
	return srv
}

func assertAutomationRoleRequestStatus(t *testing.T, srv *Server, auth string, workspaceID string, method string, path string, body *bytes.Buffer, expected int) {
	t.Helper()
	if body == nil {
		body = bytes.NewBuffer(nil)
	}
	req := httptest.NewRequest(method, path, body)
	req.Header.Set("Authorization", auth)
	req.Header.Set("X-Workspace-Id", workspaceID)
	rec := httptest.NewRecorder()
	srv.handler.ServeHTTP(rec, req)
	require.Equal(t, expected, rec.Code, rec.Body.String())
}
