package agents

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

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/store/plan"
)

func TestMeshXRealizerRealizesPresetRole(t *testing.T) {
	var gotPath string
	var gotBody meshXPresetRoleRealizationRequest
	var gotClaims map[string]any
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotPath = r.URL.Path
		require.Equal(t, "wsp_1", r.Header.Get(meshXWorkspaceHeader))
		require.NotEmpty(t, r.Header.Get(meshXTrustedJWTHeader))
		gotClaims = decodeTestJWTClaims(t, r.Header.Get("Authorization"))
		require.NoError(t, json.NewDecoder(r.Body).Decode(&gotBody))
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{
			"role": {"id": "role_from_preset"},
			"realization": {
				"agentId": "agt_1",
				"items": [{
					"manifestItemType": "role_definition_asset",
					"manifestItemName": "IDENTITY.md",
					"action": "materialize",
					"status": "realized",
					"required": true
				}]
			}
		}`))
	}))
	defer server.Close()
	realizer := NewMeshXRealizer(MeshXRealizerOptions{
		BaseURL:  server.URL + "/meshx",
		TokenTTL: time.Hour,
		Now:      func() time.Time { return time.Unix(100, 0) },
	})

	result, err := realizer.RealizeRoleForAgent(t.Context(), RealizeRoleForAgentInput{
		UserID:          "usr_1",
		WorkspaceID:     "wsp_1",
		AgentID:         "agt_1",
		Source:          Source{Kind: "preset", PresetID: "backend", Locale: "zh-CN"},
		RoleDisplayName: "Backend",
		Realization:     RealizationOptions{Apply: true, StorageScope: "workspace", TemplateLocale: "zh-CN"},
	})
	require.NoError(t, err)
	require.Equal(t, "/meshx/api/v1/roles/presets:realize", gotPath)
	require.Equal(t, "backend", gotBody.PresetID)
	require.Equal(t, "zh-CN", gotBody.Locale)
	require.Equal(t, "Backend", gotBody.DisplayName)
	require.Equal(t, "agt_1", gotBody.AgentID)
	require.Equal(t, "workspace", gotBody.StorageScope)
	require.Equal(t, "role_from_preset", result.RoleID)
	require.Equal(t, "agt_1", result.AgentID)
	require.Len(t, result.Items, 1)
	require.Equal(t, "role_definition_asset", result.Items[0].ManifestItemType)
	require.Equal(t, "usr_1", gotClaims["sub"])
	require.Equal(t, []any{"wsp_1"}, gotClaims["workspace_ids"])
	require.Equal(t, []any{"agt_1"}, gotClaims["owned_agent_ids"])
}

func TestMeshXRealizerRealizesMyRole(t *testing.T) {
	var gotPath string
	var gotBody meshXRoleRealizationRequest
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotPath = r.URL.Path
		require.NoError(t, json.NewDecoder(r.Body).Decode(&gotBody))
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{
			"roleId": "role_1",
			"agentId": "agt_1",
			"items": [
				{"manifestItemType": "role_definition_asset", "action": "copy", "status": "realized"},
				{"manifestItemType": "memory", "action": "bind", "status": "realized"}
			]
		}`))
	}))
	defer server.Close()
	realizer := NewMeshXRealizer(MeshXRealizerOptions{BaseURL: server.URL})

	result, err := realizer.RealizeRoleForAgent(t.Context(), RealizeRoleForAgentInput{
		UserID:      "usr_1",
		WorkspaceID: "wsp_1",
		AgentID:     "agt_1",
		Source:      Source{Kind: "my-role", RoleID: "role_1", VersionID: "rv_1"},
		Realization: RealizationOptions{Apply: true, SkipOptional: true},
	})
	require.NoError(t, err)
	require.Equal(t, "/api/v1/roles/role_1/realize", gotPath)
	require.Equal(t, "agt_1", gotBody.AgentID)
	require.Equal(t, "rv_1", gotBody.RoleVersionID)
	require.True(t, gotBody.SkipOptional)
	require.Equal(t, "role_1", result.RoleID)
	require.Len(t, result.Items, 1)
	require.Equal(t, "role_definition_asset", result.Items[0].ManifestItemType)
}

func TestMeshXRealizerLoadsRoleMaterialization(t *testing.T) {
	var gotPath string
	var gotLocale string
	var gotVersion string
	var gotClaims map[string]any
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotPath = r.URL.Path
		gotLocale = r.URL.Query().Get("locale")
		gotVersion = r.URL.Query().Get("versionId")
		gotClaims = decodeTestJWTClaims(t, r.Header.Get("Authorization"))
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{
			"id": "role_1",
			"roleVersionId": "rlv_1",
			"rolePackageManifest": {
				"items": [
					{"type": "role_definition_asset", "name": "IDENTITY.md", "content": "# Reviewer", "required": true},
					{"type": "portable_knowledge", "name": "Runbook", "content": "check rollback"},
					{"type": "memory", "name": "Memory", "action": "bind", "resourceId": "mem_1"}
				]
			}
		}`))
	}))
	defer server.Close()
	realizer := NewMeshXRealizer(MeshXRealizerOptions{BaseURL: server.URL + "/meshx"})

	materialization, err := realizer.RoleMaterialization(t.Context(), RealizeRoleForAgentInput{
		UserID:      "usr_1",
		WorkspaceID: "wsp_1",
		AgentID:     "agt_1",
		Source:      Source{Kind: "preset", PresetID: "reviewer", Locale: "zh-CN"},
	})
	require.NoError(t, err)
	require.Equal(t, "/meshx/api/v1/roles/presets/reviewer", gotPath)
	require.Equal(t, "zh-CN", gotLocale)
	require.Empty(t, gotVersion)
	require.Equal(t, []any{"agt_1"}, gotClaims["owned_agent_ids"])
	require.Equal(t, "role_1", materialization.RoleID)
	require.Equal(t, "rlv_1", materialization.RoleVersionID)
	require.Equal(t, "memory/system/identity.md", materialization.ActiveDefinitionPath)
	require.Len(t, materialization.ManifestItems, 2)
	require.Equal(t, "# Reviewer", materialization.ManifestItems[0].Content)
	require.Equal(t, "portable_knowledge", materialization.ManifestItems[1].Type)
}

func TestMeshXRealizerLoadsMyRoleMaterializationVersion(t *testing.T) {
	var gotPath string
	var gotVersion string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotPath = r.URL.Path
		gotVersion = r.URL.Query().Get("versionId")
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{
			"id": "role_1",
			"roleVersionId": "rv_1",
			"rolePackageManifest": {
				"items": [
					{"type": "role_definition_asset", "name": "IDENTITY.md", "content": "# Reviewer", "required": true}
				]
			}
		}`))
	}))
	defer server.Close()
	realizer := NewMeshXRealizer(MeshXRealizerOptions{BaseURL: server.URL})

	materialization, err := realizer.RoleMaterialization(t.Context(), RealizeRoleForAgentInput{
		UserID:      "usr_1",
		WorkspaceID: "wsp_1",
		AgentID:     "agt_1",
		Source:      Source{Kind: "my-role", RoleID: "role_1", VersionID: "rv_1"},
	})

	require.NoError(t, err)
	require.Equal(t, "/api/v1/roles/role_1", gotPath)
	require.Equal(t, "rv_1", gotVersion)
	require.Equal(t, "rv_1", materialization.RoleVersionID)
}

func TestMeshXRealizerLoadsPresetRoleImpact(t *testing.T) {
	var gotPath string
	var gotBody meshXPresetRoleRealizationRequest
	var gotClaims map[string]any
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotPath = r.URL.Path
		gotClaims = decodeTestJWTClaims(t, r.Header.Get("Authorization"))
		require.Equal(t, "wsp_1", r.Header.Get(meshXWorkspaceHeader))
		require.NoError(t, json.NewDecoder(r.Body).Decode(&gotBody))
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"managedMemories":1,"skillRepositories":2}`))
	}))
	defer server.Close()
	planner, ok := NewMeshXRealizer(MeshXRealizerOptions{BaseURL: server.URL + "/meshx"}).(RoleResourcePlanner)
	require.True(t, ok)

	impact, err := planner.RoleResourceImpact(t.Context(), RealizeRoleForAgentInput{
		UserID:          "usr_1",
		WorkspaceID:     "wsp_1",
		AgentID:         "agt_1",
		Source:          Source{Kind: "preset", PresetID: "backend", Locale: "zh-CN"},
		RoleDisplayName: "Backend",
		Realization:     RealizationOptions{Apply: true, StorageScope: "workspace"},
	})

	require.NoError(t, err)
	require.Equal(t, "/meshx/api/v1/internal/roles/presets:realization-impact", gotPath)
	require.Equal(t, "backend", gotBody.PresetID)
	require.Equal(t, "agt_1", gotBody.AgentID)
	require.True(t, gotBody.Apply)
	require.Equal(t, "workspace", gotBody.StorageScope)
	require.Equal(t, []any{"agt_1"}, gotClaims["owned_agent_ids"])
	require.Equal(t, ResourceImpact{ManagedMemories: 1, SkillRepositories: 2}, impact)
}

func TestMeshXRealizerCountsWorkspaceResource(t *testing.T) {
	var gotPath string
	var gotClaims map[string]any
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotPath = r.URL.Path
		gotClaims = decodeTestJWTClaims(t, r.Header.Get("Authorization"))
		require.Equal(t, http.MethodGet, r.Method)
		require.Equal(t, "application/json", r.Header.Get("Accept"))
		require.Equal(t, "wsp_1", r.Header.Get(meshXWorkspaceHeader))
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"total":9}`))
	}))
	defer server.Close()
	planner, ok := NewMeshXRealizer(MeshXRealizerOptions{BaseURL: server.URL}).(RoleResourcePlanner)
	require.True(t, ok)

	total, err := planner.CountWorkspaceResource(t.Context(), RealizeRoleForAgentInput{
		UserID:      "usr_1",
		WorkspaceID: "wsp_1",
		AgentID:     "agt_1",
	}, plan.MetricSkillRepositories, plan.QuotaScopeWorkspaceMember)

	require.NoError(t, err)
	require.Equal(t, "/api/v1/internal/skills/count", gotPath)
	require.Equal(t, []any{"agt_1"}, gotClaims["owned_agent_ids"])
	require.EqualValues(t, 9, total)
}

func TestMeshXRealizerRequiresConfiguredBaseURL(t *testing.T) {
	require.Nil(t, NewMeshXRealizer(MeshXRealizerOptions{}))
}

func decodeTestJWTClaims(t *testing.T, authorization string) map[string]any {
	t.Helper()
	const prefix = "Bearer "
	require.True(t, strings.HasPrefix(authorization, prefix), "Authorization must be a bearer token")
	parts := strings.Split(strings.TrimPrefix(authorization, prefix), ".")
	require.Len(t, parts, 3)
	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
	require.NoError(t, err)
	var claims map[string]any
	require.NoError(t, json.Unmarshal(payload, &claims))
	return claims
}
