package exec

import (
	"encoding/json"
	"testing"

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

func TestRuntimeCatalogMergesBuiltinAndExtensions(t *testing.T) {
	builtin := mustDefaultCatalog(t)
	extensions := validExtensionSnapshot("github")
	catalog, err := NewRuntimeCatalog(builtin, extensions)
	require.NoError(t, err)

	action, ok := catalog.Lookup("github", "list-items")
	require.True(t, ok)
	require.Equal(t, RuntimeActionExtension, action.Source)
	require.Equal(t, "read", action.Safety.Class)
	require.Equal(t, extensions.ETag(), catalog.ExtensionSnapshot().ETag())

	builtinAction, ok := catalog.Lookup("task", "get")
	require.True(t, ok)
	require.Equal(t, RuntimeActionBuiltin, builtinAction.Source)
}

func TestRuntimeCatalogIsolatesReservedDomain(t *testing.T) {
	catalog, err := NewRuntimeCatalog(
		mustDefaultCatalog(t),
		validExtensionSnapshot("task"),
	)
	require.NoError(t, err)
	require.NotEmpty(t, catalog.Diagnostics())
	action, ok := catalog.Lookup("task", "get")
	require.True(t, ok)
	require.Equal(t, RuntimeActionBuiltin, action.Source)
	_, ok = catalog.Lookup("task", "list-items")
	require.False(t, ok)
}

func TestRuntimeCatalogInvalidSnapshotFailsClosedToBuiltins(t *testing.T) {
	invalid := NewExtensionSnapshot(`"ext-v1-invalid"`, []ExtensionDomain{{
		ExtensionName: "github",
		DisplayName:   "GitHub",
		Actions: []ExtensionAction{{
			ExtensionName: "other",
			DisplayName:   "GitHub",
			Name:          "list-items",
			Safety:        ExtensionSafety{Class: "read"},
			InputSchema:   map[string]any{"type": "object"},
			OutputSchema:  map[string]any{"type": "object"},
		}},
	}})
	catalog, err := NewRuntimeCatalog(mustDefaultCatalog(t), invalid)
	require.NoError(t, err)
	require.NotEmpty(t, catalog.Diagnostics())
	_, ok := catalog.Lookup("github", "list-items")
	require.False(t, ok)
	require.True(t, catalog.ExtensionSnapshot().Empty())
	require.Equal(t, invalid.ETag(), catalog.ExtensionSnapshot().ETag())
}

func TestRuntimeCatalogCopiesDescriptors(t *testing.T) {
	snapshot := validExtensionSnapshot("github")
	catalog, err := NewRuntimeCatalog(mustDefaultCatalog(t), snapshot)
	require.NoError(t, err)

	domains := snapshot.Domains()
	domains[0].ExtensionName = "mutated"
	domains[0].Actions[0].InputSchema["type"] = "string"
	action, ok := catalog.Lookup("github", "list-items")
	require.True(t, ok)
	extension, ok := action.Extension()
	require.True(t, ok)
	require.Equal(t, "object", extension.InputSchema["type"])
}

func TestRuntimeCatalogViewContainsNoCatalogIdentity(t *testing.T) {
	catalog, err := NewRuntimeCatalog(
		mustDefaultCatalog(t),
		validExtensionSnapshot("github"),
	)
	require.NoError(t, err)
	view, err := catalog.View()
	require.NoError(t, err)
	document := runtimeCatalogDocumentFromView(view)
	raw, err := json.Marshal(document)
	require.NoError(t, err)
	require.NotContains(t, string(raw), "extensionCatalogVersion")
	require.NotContains(t, string(raw), "extensionCatalogDigest")
}

func validExtensionSnapshot(name string) ExtensionSnapshot {
	return NewExtensionSnapshot(`"ext-v1-`+name+`"`, []ExtensionDomain{{
		ExtensionName: name,
		DisplayName:   name,
		Actions: []ExtensionAction{{
			ExtensionName: name,
			DisplayName:   name,
			Name:          "list-items",
			Summary:       "List items",
			Safety:        ExtensionSafety{Class: "read"},
			InputSchema:   map[string]any{"type": "object"},
			OutputSchema:  map[string]any{"type": "object"},
		}},
	}})
}

func testExtensionSnapshot(t *testing.T, domains []ExtensionDomain) ExtensionSnapshot {
	t.Helper()
	snapshot := NewExtensionSnapshot(`"ext-v1-test"`, domains)
	require.NoError(t, ValidateExtensionSnapshot(snapshot))
	return snapshot
}
