package agent

import (
	"context"
	"encoding/json"
	"net/http"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/service/approvalapp"
	"github.com/tidbcloud/AgentTeam/internal/service/team/roleproposal"
	storeagent "github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/app"
	"github.com/tidbcloud/AgentTeam/internal/store/approval"
)

type fakeAgentTeamStore struct {
	listQuery   string
	listLimit   int
	listOptions AgentTeamListOptions
	listPage    AgentTeamListPage
	listErr     error
	getID       string
	teams       []AgentTeamSummary
	team        AgentTeamDetail
}

func (store *fakeAgentTeamStore) ListTeamsForAgent(_ context.Context, _ storeagent.AuthenticatedAgent, options AgentTeamListOptions) (AgentTeamListPage, error) {
	store.listOptions = options
	store.listQuery, store.listLimit = options.Query, options.Limit
	if store.listErr != nil {
		return AgentTeamListPage{}, store.listErr
	}
	if store.listPage.Teams != nil {
		return store.listPage, nil
	}
	return AgentTeamListPage{Teams: store.teams}, nil
}

func (store *fakeAgentTeamStore) GetTeamForAgent(_ context.Context, _ storeagent.AuthenticatedAgent, teamID string) (AgentTeamDetail, error) {
	store.getID = teamID
	return store.team, nil
}

type fakeAgentTeamApprovalRuntime struct {
	command approvalapp.SubmitCommand
	result  approvalapp.SubmitResult
}

func (runtime *fakeAgentTeamApprovalRuntime) SubmitRequest(_ context.Context, _ string, command approvalapp.SubmitCommand) (approvalapp.SubmitResult, error) {
	runtime.command = command
	return runtime.result, nil
}
func (*fakeAgentTeamApprovalRuntime) PresentPending(context.Context, approvalapp.SubmitResult) (app.AppNotificationResult, error) {
	return app.AppNotificationResult{}, nil
}

func agentTeamTestRouter(store AgentTeamStore, approvals ApprovalRuntime) *gin.Engine {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	RegisterRoutes(router, Dependencies{
		Auth: fakeAgentAuthenticator{agent: storeagent.AuthenticatedAgent{
			AgentID: "agt_0000000000test", WorkspaceID: "wsp_test", MemberID: "00000000test",
		}},
		Teams: store, ApprovalSubjects: approvals,
	})
	return router
}

func TestAgentTeamRoutesExposeMinimalContract(t *testing.T) {
	router := agentTeamTestRouter(&fakeAgentTeamStore{}, &fakeAgentTeamApprovalRuntime{})
	got := map[string]bool{}
	for _, route := range router.Routes() {
		got[route.Method+" "+route.Path] = true
	}
	base := "/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/teams"
	require.True(t, got[http.MethodGet+" "+base])
	require.True(t, got[http.MethodPost+" "+base+"/get"])
	require.True(t, got[http.MethodPost+" "+base+"/update"])
}

func TestAgentTeamListReturnsOnlyMinimalFields(t *testing.T) {
	store := &fakeAgentTeamStore{teams: []AgentTeamSummary{
		{
			TeamID: "team_1", Handle: "backend", DisplayName: "Backend Team",
			Description: "Build", Role: "leader",
		},
		{
			TeamID: "team_2", Handle: "fallback", DisplayName: "",
			Description: "Fallback", Role: "member",
		},
	}}
	response := agentTaskRequest(t, agentTeamTestRouter(store, nil), http.MethodGet, "/teams?q=back&limit=7", nil)
	require.Equal(t, http.StatusOK, response.Code, response.Body.String())
	require.Equal(t, "back", store.listQuery)
	require.Equal(t, 7, store.listLimit)
	require.Equal(t, "back", store.listOptions.Query)
	var body map[string]any
	require.NoError(t, json.Unmarshal(response.Body.Bytes(), &body))
	teams := body["teams"].([]any)
	team := teams[0].(map[string]any)
	require.Equal(t, map[string]any{
		"teamId": "team_1", "name": "Backend Team", "handle": "backend", "description": "Build", "role": "leader",
	}, team)
	require.Equal(t, "fallback", teams[1].(map[string]any)["name"])
}

func TestAgentTeamGetReturnsMinimalMemberConfiguration(t *testing.T) {
	store := &fakeAgentTeamStore{
		team: AgentTeamDetail{TeamID: "team_1", Handle: "backend", DisplayName: "Backend Team", Description: "Build",
			Members: []AgentTeamMember{{TeamMemberID: "wtm_1", AgentID: "agt_0000000builder", DisplayName: "Builder", RoleLabel: "Engineer", RoleOverlayPrompt: "Implement", WakeOnTeamMention: true}},
		},
	}
	response := agentTaskRequest(t, agentTeamTestRouter(store, nil), http.MethodPost, "/teams/get", map[string]any{"teamId": "team_1"})
	require.Equal(t, http.StatusOK, response.Code, response.Body.String())
	require.Equal(t, "team_1", store.getID)
	require.NotContains(t, response.Body.String(), `"role"`)
	require.NotContains(t, response.Body.String(), "sortOrder")
	require.NotContains(t, response.Body.String(), "version")
	require.NotContains(t, response.Body.String(), `"handle"`)
	require.NotContains(t, response.Body.String(), `"displayName":"Backend Team"`)
	require.Contains(t, response.Body.String(), `"name":"Backend Team"`)
	require.Contains(t, response.Body.String(), `"agentId":"agt_0000000builder"`)
}

func TestAgentTeamUpdateUsesApprovalAndRejectsStructuralFields(t *testing.T) {
	approvals := &fakeAgentTeamApprovalRuntime{result: approvalapp.SubmitResult{
		Instance: approval.Instance{ID: "appr_1", SubjectRef: "team_1", State: approval.StatePending, Version: 1}, Created: true,
	}}
	body := map[string]any{
		"teamId": "team_1", "requestId": "req_1", "description": "New description",
		"memberUpdates": []map[string]any{{"teamMemberId": "wtm_1", "roleLabel": "Engineer", "roleOverlayPrompt": "Implement", "wakeOnTeamMention": true}},
	}
	response := agentTaskRequest(t, agentTeamTestRouter(&fakeAgentTeamStore{}, approvals), http.MethodPost, "/teams/update", body)
	require.Equal(t, http.StatusOK, response.Code, response.Body.String())
	require.Equal(t, "team_1", approvals.command.SubjectRef)
	require.Equal(t, "req_1", approvals.command.ClientRequestID)
	payload, ok := approvals.command.Payload.(roleproposal.SubmitInput)
	require.True(t, ok)
	require.NotNil(t, payload.Description)
	require.Equal(t, "New description", *payload.Description)
	require.Equal(t, "Engineer", *payload.Members[0].RoleLabel)
	require.True(t, *payload.Members[0].WakeOnTeamMention)

	forbidden := agentTaskRequest(t, agentTeamTestRouter(&fakeAgentTeamStore{}, approvals), http.MethodPost, "/teams/update", map[string]any{
		"teamId": "team_1", "requestId": "req_2", "leaderId": "agt_000000000other",
	})
	require.Equal(t, http.StatusBadRequest, forbidden.Code)
}

func TestAgentTeamListCapsLimit(t *testing.T) {
	store := &fakeAgentTeamStore{}
	response := agentTaskRequest(t, agentTeamTestRouter(store, nil), http.MethodGet, "/teams?limit=101", nil)
	require.Equal(t, http.StatusOK, response.Code)
	require.Equal(t, 100, store.listLimit)
}

var _ = time.Time{}
