package s3tables

import (
	"encoding/json"
	"reflect"
	"strings"
	"testing"
)

func TestValidateNamespaceSupportsMultiLevel(t *testing.T) {
	got, err := validateNamespace([]string{"analytics", "daily"})
	if err != nil {
		t.Fatalf("validateNamespace returned error: %v", err)
	}
	if got != "analytics.daily" {
		t.Fatalf("validateNamespace = %q, want %q", got, "analytics.daily")
	}
}

func TestValidateNamespaceSupportsDottedInput(t *testing.T) {
	got, err := validateNamespace([]string{"analytics.daily"})
	if err != nil {
		t.Fatalf("validateNamespace returned error: %v", err)
	}
	if got != "analytics.daily" {
		t.Fatalf("validateNamespace = %q, want %q", got, "analytics.daily")
	}
}

func TestValidateNamespaceRejectsEmptyDottedSegment(t *testing.T) {
	_, err := validateNamespace([]string{"analytics..daily"})
	if err == nil {
		t.Fatalf("expected validateNamespace to fail for empty dotted segment")
	}
}

func TestParseNamespace(t *testing.T) {
	tests := []struct {
		name      string
		namespace string
		want      []string
		wantErr   bool
	}{
		{
			name:      "single level",
			namespace: "analytics",
			want:      []string{"analytics"},
		},
		{
			name:      "multi level dotted",
			namespace: "analytics.daily",
			want:      []string{"analytics", "daily"},
		},
		{
			name:      "invalid reserved prefix",
			namespace: "analytics.awsprod",
			wantErr:   true,
		},
		{
			name:      "invalid empty segment",
			namespace: "analytics..daily",
			wantErr:   true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := ParseNamespace(tt.namespace)
			if tt.wantErr {
				if err == nil {
					t.Fatalf("ParseNamespace(%q) expected error", tt.namespace)
				}
				return
			}
			if err != nil {
				t.Fatalf("ParseNamespace(%q) unexpected error: %v", tt.namespace, err)
			}
			if len(got) != len(tt.want) {
				t.Fatalf("ParseNamespace(%q) = %v, want %v", tt.namespace, got, tt.want)
			}
			for i := range got {
				if got[i] != tt.want[i] {
					t.Fatalf("ParseNamespace(%q) = %v, want %v", tt.namespace, got, tt.want)
				}
			}
		})
	}
}

func TestParseTableFromARNWithMultiLevelNamespace(t *testing.T) {
	arn := "arn:aws:s3tables:us-east-1:123456789012:bucket/testbucket/table/analytics.daily/events"
	bucket, namespace, table, err := parseTableFromARN(arn)
	if err != nil {
		t.Fatalf("parseTableFromARN returned error: %v", err)
	}
	if bucket != "testbucket" {
		t.Fatalf("bucket = %q, want %q", bucket, "testbucket")
	}
	if namespace != "analytics.daily" {
		t.Fatalf("namespace = %q, want %q", namespace, "analytics.daily")
	}
	if table != "events" {
		t.Fatalf("table = %q, want %q", table, "events")
	}
}

func TestBuildTableARNWithDottedNamespace(t *testing.T) {
	arn, err := BuildTableARN("us-east-1", "123456789012", "testbucket", "analytics.daily", "events")
	if err != nil {
		t.Fatalf("BuildTableARN returned error: %v", err)
	}
	if !strings.Contains(arn, "/table/analytics.daily/events") {
		t.Fatalf("BuildTableARN returned %q, missing normalized namespace/table path", arn)
	}
}

func TestExpandNamespace(t *testing.T) {
	got := expandNamespace("analytics.daily")
	want := []string{"analytics", "daily"}
	if len(got) != len(want) {
		t.Fatalf("expandNamespace = %v, want %v", got, want)
	}
	for i := range got {
		if got[i] != want[i] {
			t.Fatalf("expandNamespace = %v, want %v", got, want)
		}
	}
}

func TestNamespaceMetadataPropertiesRoundTrip(t *testing.T) {
	testCases := []struct {
		name     string
		metadata namespaceMetadata
	}{
		{
			name: "with properties",
			metadata: namespaceMetadata{
				Namespace:      []string{"analytics"},
				Properties:     map[string]string{"owner": "finance"},
				OwnerAccountID: "123456789012",
			},
		},
		{
			name: "nil properties",
			metadata: namespaceMetadata{
				Namespace:      []string{"analytics"},
				Properties:     nil,
				OwnerAccountID: "123456789012",
			},
		},
		{
			name: "empty properties",
			metadata: namespaceMetadata{
				Namespace:      []string{"analytics"},
				Properties:     map[string]string{},
				OwnerAccountID: "123456789012",
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			data, err := json.Marshal(tc.metadata)
			if err != nil {
				t.Fatalf("json.Marshal(metadata) returned error: %v", err)
			}

			var decoded namespaceMetadata
			if err := json.Unmarshal(data, &decoded); err != nil {
				t.Fatalf("json.Unmarshal(data) returned error: %v", err)
			}

			// Due to `omitempty`, nil and empty maps are unmarshaled as nil.
			if len(tc.metadata.Properties) == 0 {
				if decoded.Properties != nil {
					t.Fatalf("expected nil properties for empty/nil input, got %v", decoded.Properties)
				}
				return
			}

			if !reflect.DeepEqual(decoded.Properties, tc.metadata.Properties) {
				t.Fatalf("decoded.Properties = %v, want %v", decoded.Properties, tc.metadata.Properties)
			}
		})
	}
}
