package registry

import (
	"reflect"
	"strings"
	"testing"
)

func TestParseStoredManifestDefaultsAndSortedScopes(t *testing.T) {
	manifest, err := ParseStoredManifest([]byte(`{
		"scopes": ["threads.read", " messages.write ", "", "threads.read"],
		"config_schema": {"type": "object", "additionalProperties": false}
	}`))
	if err != nil {
		t.Fatalf("ParseStoredManifest returned error: %v", err)
	}
	if got, want := manifest.Scopes, []string{"messages.write", "threads.read"}; len(got) != len(want) || got[0] != want[0] || got[1] != want[1] {
		t.Fatalf("scopes = %#v, want %#v", got, want)
	}
	if manifest.ConfigSchema == DefaultManifestObjectSchema {
		t.Fatalf("config schema used default, want stored schema")
	}
	if manifest.CredentialsSchema != DefaultManifestObjectSchema {
		t.Fatalf("credentials schema = %q, want default %q", manifest.CredentialsSchema, DefaultManifestObjectSchema)
	}
}

func TestParseStoredManifestRejectsInvalidScopesShape(t *testing.T) {
	if _, err := ParseStoredManifest([]byte(`{"scopes": "messages.read"}`)); err == nil {
		t.Fatal("expected invalid scopes shape to return error")
	}
}

func TestParseStoredManifestMergesAppScopesAndConfigSchema(t *testing.T) {
	manifest, err := ParseStoredManifest([]byte(`{
		"scopes": ["profile.read"],
		"config_schema": {"type": "object", "properties": {"legacy": {"type": "string"}}},
		"capabilities": {
			"runtime": {
				"runtime": {
					"transports": ["runner_stream"],
					"default_transport": "runner_stream"
				},
				"events": ["agentteam.message.mention"],
				"scopes": ["messages.write", "app.mentions.read", "messages.write"],
				"config_schema": {
					"type": "object",
					"properties": {
						"environment": {"type": "string"}
					}
				}
			}
		}
	}`))
	if err != nil {
		t.Fatalf("ParseStoredManifest returned error: %v", err)
	}
	if got, want := manifest.Scopes, []string{"app.mentions.read", "messages.write", "profile.read"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("scopes = %#v, want %#v", got, want)
	}
	if !strings.Contains(manifest.ConfigSchema, "environment") {
		t.Fatalf("config schema = %s, want app config schema", manifest.ConfigSchema)
	}
	if strings.Contains(manifest.ConfigSchema, "legacy") {
		t.Fatalf("config schema = %s, want app config schema instead of top-level schema", manifest.ConfigSchema)
	}
}

func TestParseStoredManifestIgnoresTopLevelOnlyAppScopes(t *testing.T) {
	manifest, err := ParseStoredManifest([]byte(`{
		"scopes": ["app.mentions.read", "messages.write"],
		"capabilities": {
			"runtime": {
				"runtime": {"transports": ["runner_stream"]},
				"events": [],
				"scopes": ["messages.write", "app.notifications.write"],
				"notifications": {"targets": ["human_dm"], "content": ["message"]}
			}
		}
	}`))
	if err != nil {
		t.Fatalf("ParseStoredManifest returned error: %v", err)
	}
	if got, want := manifest.Scopes, []string{"app.notifications.write", "messages.write"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("scopes = %#v, want %#v", got, want)
	}
}

func TestParseStoredAppRuntimeCapabilityReturnsNormalizedView(t *testing.T) {
	app, err := ParseStoredAppRuntimeCapability([]byte(`{
		"capabilities": {
			"runtime": {
				"runtime": {
					"transports": ["callback", "runner_stream", "callback"],
					"default_transport": "runner_stream"
				},
				"events": ["agentteam.message.mention", "agentteam.message.mention"],
				"scopes": ["messages.write", "app.mentions.read"],
				"config_schema": {"type": "object"}
			}
		}
	}`))
	if err != nil {
		t.Fatalf("ParseStoredAppRuntimeCapability returned error: %v", err)
	}
	if !app.Supported {
		t.Fatal("Supported = false, want true")
	}
	if got, want := app.Transports, []string{"callback", "runner_stream"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("transports = %#v, want %#v", got, want)
	}
	if got, want := app.Events, []string{"agentteam.message.mention"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("events = %#v, want %#v", got, want)
	}
	if got, want := app.Scopes, []string{"app.mentions.read", "messages.write"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("scopes = %#v, want %#v", got, want)
	}
	if app.DefaultTransport != "runner_stream" {
		t.Fatalf("DefaultTransport = %q, want runner_stream", app.DefaultTransport)
	}
}

func TestParseStoredAppRuntimeCapabilityRejectsUnsupportedEvent(t *testing.T) {
	_, err := ParseStoredAppRuntimeCapability([]byte(`{
		"capabilities": {
			"runtime": {
				"runtime": {
					"transports": ["callback"]
				},
				"events": ["agentteam.message.mention", "agentteam.unknown"],
				"scopes": ["messages.write", "app.mentions.read"]
			}
		}
	}`))
	if err == nil || !strings.Contains(err.Error(), "agentteam.unknown") {
		t.Fatalf("ParseStoredAppRuntimeCapability error = %v, want unsupported event error", err)
	}
}

func TestParseStoredAppRuntimeCapabilityReturnsNotificationView(t *testing.T) {
	app, err := ParseStoredAppRuntimeCapability([]byte(`{
		"capabilities": {
			"runtime": {
				"runtime": {"transports": ["callback"]},
				"events": ["agentteam.app.notification.action"],
				"scopes": ["messages.write", "app.notifications.write", "app.notification_actions.read"],
				"notifications": {
					"targets": ["human_dm", "bound_scope", "human_dm"],
					"content": ["message", "interactive_card"]
				}
			}
		}
	}`))
	if err != nil {
		t.Fatalf("ParseStoredAppRuntimeCapability returned error: %v", err)
	}
	if !app.Notifications.Supported {
		t.Fatal("Notifications.Supported = false, want true")
	}
	if got, want := app.Notifications.Targets, []string{"human_dm", "bound_scope"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("targets = %#v, want %#v", got, want)
	}
	if got, want := app.Notifications.Content, []string{"message", "interactive_card"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("content = %#v, want %#v", got, want)
	}
}

func TestParseStoredAppRuntimeCapabilityAllowsInternalNotificationAppWithoutExternalRuntime(t *testing.T) {
	app, err := ParseStoredAppRuntimeCapability([]byte(`{
		"capabilities": {
			"runtime": {
				"events": ["agentteam.app.notification.action"],
				"scopes": ["messages.write", "app.notifications.write", "app.notification_actions.read"],
				"notifications": {
					"targets": ["human_dm"],
					"content": ["message", "interactive_card"]
				}
			}
		}
	}`))
	if err != nil {
		t.Fatalf("ParseStoredAppRuntimeCapability returned error: %v", err)
	}
	if !app.Supported || !app.Notifications.Supported {
		t.Fatalf("app = %#v, want supported internal notification App", app)
	}
	if len(app.Transports) != 0 || app.DefaultTransport != "" {
		t.Fatalf("runtime = %#v/%q, want no external transport", app.Transports, app.DefaultTransport)
	}
}

func TestParseStoredAppRuntimeCapabilityRejectsMentionAppWithoutExternalRuntime(t *testing.T) {
	_, err := ParseStoredAppRuntimeCapability([]byte(`{
		"capabilities": {
			"runtime": {
				"events": ["agentteam.message.mention"],
				"scopes": ["messages.write", "app.mentions.read"]
			}
		}
	}`))
	if err == nil || !strings.Contains(err.Error(), "runtime is required for agentteam.message.mention") {
		t.Fatalf("ParseStoredAppRuntimeCapability error = %v, want missing runtime error", err)
	}
}

func TestParseStoredAppRuntimeCapabilityRejectsUnsupportedNotificationValue(t *testing.T) {
	_, err := ParseStoredAppRuntimeCapability([]byte(`{
		"capabilities": {
			"runtime": {
				"runtime": {"transports": ["callback"]},
				"events": [],
				"scopes": [],
				"notifications": {"targets": ["human_dm"], "content": ["video"]}
			}
		}
	}`))
	if err == nil || !strings.Contains(err.Error(), "video") {
		t.Fatalf("ParseStoredAppRuntimeCapability error = %v, want unsupported content error", err)
	}
}
