package extension

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/extension/registry"
	"github.com/tidbcloud/AgentTeam/internal/extension/spec"
)

var secretBindingMarkers = []string{
	"https://secret-endpoint-marker.example.com",
	"secret-profile-id-marker",
	"SECRET-HTTP-METHOD",
	"/secret/binding/path/marker",
	"secret-required-scope-marker",
	"secret-static-header-value-marker",
	"secret-auth-default-marker",
}

func extensionWithBinding() registry.RegisteredExtension {
	return registry.RegisteredExtension{
		Kind: registry.External,
		Definition: registry.Definition{
			Domain:      "github",
			Version:     1,
			Digest:      "sha256:definition-digest-marker",
			Name:        "GitHub",
			Description: "GitHub REST actions curated for AgentTeam Agents.",
			Actions: []registry.DefinitionAction{
				{
					Name:        "create-issue",
					Summary:     "Create an issue",
					Description: "Create one issue in a repository.",
					Safety: spec.Safety{
						Class: "mutation",
					},
					InputSchema: map[string]any{
						"$schema":  "https://json-schema.org/draft/2020-12/schema",
						"type":     "object",
						"required": []string{"title"},
						"properties": map[string]any{
							"title": map[string]any{
								"type": "string", "minLength": 1, "maxLength": 256,
							},
						},
					},
					OutputSchema: map[string]any{"type": "object"},
					Example: map[string]any{
						"owner": "pingcap",
						"repo":  "agentteam",
						"title": "Add Extension support",
					},
				},
				{
					Name:         "list-issues",
					Summary:      "List issues",
					Safety:       spec.Safety{Class: "read"},
					InputSchema:  map[string]any{"type": "object"},
					OutputSchema: map[string]any{"type": "object"},
				},
			},
		},
		Binding: registry.ExternalBinding{
			Endpoint: spec.Endpoint{
				BaseURL: "https://secret-endpoint-marker.example.com",
			},
			Auth: spec.Auth{
				Default: "secret-auth-default-marker",
				Profiles: []spec.AuthProfile{{
					ID: "secret-profile-id-marker", Type: "oauth2_bearer",
				}},
			},
			Actions: []registry.ExternalBindingAction{{
				Name: "create-issue",
				Auth: &spec.ActionAuthRequirement{
					Profile:        "secret-profile-id-marker",
					RequiredScopes: []string{"secret-required-scope-marker"},
				},
				Binding: spec.Binding{
					Method: "SECRET-HTTP-METHOD",
					Path:   "/secret/binding/path/marker",
					StaticHeaders: map[string]string{
						"Accept": "secret-static-header-value-marker",
					},
				},
			}},
		},
	}
}

func TestProjectAuthorizedRootReturnsFullContentWithoutSecrets(t *testing.T) {
	root := projectAuthorizedRoot(
		[]registry.RegisteredExtension{extensionWithBinding()},
		[]AgentDomainAccess{{
			Domain:            "github",
			DefinitionVersion: 1,
			DefinitionDigest:  "sha256:definition-digest-marker",
			ExtensionName:     "github-work",
			Actions:           []string{"create-issue"},
		}},
	)
	require.Len(t, root.Extensions, 1)
	extension := root.Extensions[0]
	require.Equal(t, "github-work", extension.ExtensionName)
	require.Equal(t, "GitHub", extension.DisplayName)
	require.Len(t, extension.Actions, 1)
	action := extension.Actions[0]
	require.Equal(t, "create-issue", action.Name)
	require.Equal(t, "Create an issue", action.Summary)
	require.Equal(t, "mutation", action.Safety.Class)
	require.NotNil(t, action.InputSchema)
	require.NotNil(t, action.OutputSchema)
	require.NotNil(t, action.Example)

	encoded, err := json.Marshal(root)
	require.NoError(t, err)
	for _, marker := range secretBindingMarkers {
		assert.NotContains(t, string(encoded), marker)
	}
	assert.NotContains(t, string(encoded), "definitionDigest")
	assert.NotContains(t, string(encoded), "definitionVersion")
	assert.NotContains(t, string(encoded), "catalogDigest")
	assert.NotContains(t, string(encoded), "catalogVersion")
}

func TestProjectAuthorizedRootFiltersAndSorts(t *testing.T) {
	root := projectAuthorizedRoot(
		[]registry.RegisteredExtension{extensionWithBinding()},
		[]AgentDomainAccess{
			{
				Domain:            "github",
				DefinitionVersion: 1,
				DefinitionDigest:  "sha256:definition-digest-marker",
				ExtensionName:     "z-github",
				Actions:           []string{"list-issues"},
			},
			{
				Domain:            "github",
				DefinitionVersion: 1,
				DefinitionDigest:  "sha256:definition-digest-marker",
				ExtensionName:     "a-github",
				Actions:           []string{"create-issue"},
			},
			{Domain: "github", Actions: []string{"create-issue"}},
		},
	)
	require.Len(t, root.Extensions, 2)
	require.Equal(t, "a-github", root.Extensions[0].ExtensionName)
	require.Equal(t, "create-issue", root.Extensions[0].Actions[0].Name)
	require.Equal(t, "z-github", root.Extensions[1].ExtensionName)
	require.Equal(t, "list-issues", root.Extensions[1].Actions[0].Name)
}

func TestProjectAuthorizedRootUsesSelectedDefinitionIdentity(t *testing.T) {
	v1 := extensionWithBinding()
	v2 := extensionWithBinding()
	v2.Definition.Version = 2
	v2.Definition.Digest = "sha256:v2"
	v2.Definition.Name = "GitHub v2"
	v2.Definition.Actions[0].Summary = "Create an issue with v2"

	root := projectAuthorizedRoot(
		[]registry.RegisteredExtension{v1, v2},
		[]AgentDomainAccess{{
			Domain:            v1.Definition.Domain,
			DefinitionVersion: v1.Definition.Version,
			DefinitionDigest:  v1.Definition.Digest,
			ExtensionName:     "github-work",
			Actions:           []string{"create-issue"},
		}},
	)
	require.Len(t, root.Extensions, 1)
	require.Equal(t, "GitHub", root.Extensions[0].DisplayName)
	require.Equal(t, "Create an issue", root.Extensions[0].Actions[0].Summary)
}

func TestLatestDomainAndActionUseExactHighestVersion(t *testing.T) {
	v1 := extensionWithBinding()
	v2 := extensionWithBinding()
	v2.Definition.Version = 2
	v2.Definition.Digest = "sha256:v2"

	got, ok := latestDomain(
		[]registry.RegisteredExtension{v1, v2},
		"github",
	)
	require.True(t, ok)
	require.Equal(t, 2, got.Definition.Version)

	_, ok = latestDomain([]registry.RegisteredExtension{v1, v2}, "github ")
	require.False(t, ok)
	_, ok = findAction(v2, "create-issue ")
	require.False(t, ok)
}
