package utils

import (
	"testing"

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

func TestParseAWSARN(t *testing.T) {
	tests := []struct {
		name           string
		arnStr         string
		service        string
		resourcePrefix string
		wantResourceId string
		wantErr        bool
		errSubstr      string
	}{
		{
			name:           "valid KMS ARN",
			arnStr:         "arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012",
			service:        "kms",
			resourcePrefix: "key/",
			wantResourceId: "12345678-1234-1234-1234-123456789012",
		},
		{
			name:           "valid IAM role ARN",
			arnStr:         "arn:aws:iam::123456789012:role/test-role",
			service:        "iam",
			resourcePrefix: "role/",
			wantResourceId: "test-role",
		},
		{
			name:           "valid ARN no service check",
			arnStr:         "arn:aws:s3:::my-bucket",
			service:        "",
			resourcePrefix: "",
			wantResourceId: "my-bucket",
		},
		{
			name:      "empty ARN",
			arnStr:    "",
			wantErr:   true,
			errSubstr: "empty",
		},
		{
			name:      "invalid format",
			arnStr:    "not-an-arn",
			wantErr:   true,
			errSubstr: "invalid AWS ARN format",
		},
		{
			name:           "wrong service",
			arnStr:         "arn:aws:s3:::my-bucket",
			service:        "kms",
			resourcePrefix: "",
			wantErr:        true,
			errSubstr:      "service must be 'kms'",
		},
		{
			name:           "wrong resource prefix",
			arnStr:         "arn:aws:kms:us-west-2:123456789012:alias/my-alias",
			service:        "kms",
			resourcePrefix: "key/",
			wantErr:        true,
			errSubstr:      "resource must start with 'key/'",
		},
		{
			name:           "empty resource id",
			arnStr:         "arn:aws:kms:us-west-2:123456789012:key/",
			service:        "kms",
			resourcePrefix: "key/",
			wantErr:        true,
			errSubstr:      "resource id is empty",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, resourceId, err := ParseAWSARN(tt.arnStr, tt.service, tt.resourcePrefix)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
			assert.Equal(t, tt.wantResourceId, resourceId)
		})
	}
}

func TestParseAliCloudARN(t *testing.T) {
	tests := []struct {
		name           string
		arnStr         string
		service        string
		resourcePrefix string
		wantResourceId string
		wantARN        *AliCloudARN
		wantErr        bool
		errSubstr      string
	}{
		{
			name:           "valid KMS ARN",
			arnStr:         "acs:kms:cn-hangzhou:123456789:key/test-key-id",
			service:        "kms",
			resourcePrefix: "key/",
			wantResourceId: "test-key-id",
			wantARN: &AliCloudARN{
				Service:   "kms",
				Region:    "cn-hangzhou",
				AccountID: "123456789",
				Resource:  "key/test-key-id",
			},
		},
		{
			name:           "valid RAM role ARN",
			arnStr:         "acs:ram::123456789:role/test-role",
			service:        "ram",
			resourcePrefix: "role/",
			wantResourceId: "test-role",
		},
		{
			name:           "valid ARN no check",
			arnStr:         "acs:ecs:cn-hangzhou:123456789:instance/i-12345",
			service:        "",
			resourcePrefix: "",
			wantResourceId: "instance/i-12345",
		},
		{
			name:      "empty ARN",
			arnStr:    "",
			wantErr:   true,
			errSubstr: "empty",
		},
		{
			name:      "invalid format",
			arnStr:    "invalid-arn",
			wantErr:   true,
			errSubstr: "invalid AliCloud ARN format",
		},
		{
			name:           "wrong service",
			arnStr:         "acs:ecs:cn-hangzhou:123456789:instance/i-12345",
			service:        "kms",
			resourcePrefix: "",
			wantErr:        true,
			errSubstr:      "service must be 'kms'",
		},
		{
			name:           "wrong resource prefix",
			arnStr:         "acs:kms:cn-hangzhou:123456789:alias/my-alias",
			service:        "kms",
			resourcePrefix: "key/",
			wantErr:        true,
			errSubstr:      "resource must start with 'key/'",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			parsed, resourceId, err := ParseAliCloudARN(tt.arnStr, tt.service, tt.resourcePrefix)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
			assert.Equal(t, tt.wantResourceId, resourceId)
			if tt.wantARN != nil {
				assert.Equal(t, tt.wantARN.Service, parsed.Service)
				assert.Equal(t, tt.wantARN.Region, parsed.Region)
				assert.Equal(t, tt.wantARN.AccountID, parsed.AccountID)
				assert.Equal(t, tt.wantARN.Resource, parsed.Resource)
			}
		})
	}
}

func TestValidateAliCloudARN(t *testing.T) {
	tests := []struct {
		name   string
		arnStr string
		want   bool
	}{
		{"valid ARN", "acs:kms:cn-hangzhou:123456789:key/test-key", true},
		{"empty ARN", "", false},
		{"invalid format", "invalid-arn", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.want, ValidateAliCloudARN(tt.arnStr))
		})
	}
}

func TestParseAWSKmsKeyId(t *testing.T) {
	tests := []struct {
		name      string
		arnStr    string
		wantKeyId string
		wantErr   bool
		errSubstr string
	}{
		{"valid ARN", "arn:aws:kms:us-west-2:123456789012:key/12345678", "12345678", false, ""},
		{"valid aws-cn", "arn:aws-cn:kms:cn-north-1:123456789012:key/cn-key", "cn-key", false, ""},
		{"empty ARN", "", "", true, "empty"},
		{"wrong service", "arn:aws:s3:::bucket", "", true, "service must be 'kms'"},
		{"wrong resource", "arn:aws:kms:us-west-2:123456789012:alias/my-alias", "", true, "resource must start with 'key/'"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			keyId, err := ParseAWSKmsKeyId(tt.arnStr)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
			assert.Equal(t, tt.wantKeyId, keyId)
		})
	}
}

func TestValidateAWSRoleARN(t *testing.T) {
	tests := []struct {
		name      string
		arnStr    string
		wantErr   bool
		errSubstr string
	}{
		{"valid role ARN", "arn:aws:iam::123456789012:role/test-role", false, ""},
		{"valid role with path", "arn:aws:iam::123456789012:role/service-role/my-role", false, ""},
		{"empty ARN", "", true, "empty"},
		{"wrong service", "arn:aws:kms:us-west-2:123456789012:key/12345678", true, "service must be 'iam'"},
		{"wrong resource", "arn:aws:iam::123456789012:user/test-user", true, "resource must start with 'role/'"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateAWSRoleARN(tt.arnStr)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
		})
	}
}

func TestParseAliCloudKmsKeyId(t *testing.T) {
	tests := []struct {
		name      string
		arnStr    string
		wantKeyId string
		wantErr   bool
		errSubstr string
	}{
		{"valid ARN", "acs:kms:cn-hangzhou:123456789:key/test-key-id", "test-key-id", false, ""},
		{"valid empty region", "acs:kms::123456789:key/global-key", "global-key", false, ""},
		{"empty ARN", "", "", true, "empty"},
		{"wrong service", "acs:ram::123456789:role/test", "", true, "service must be 'kms'"},
		{"wrong resource", "acs:kms:cn-hangzhou:123456789:alias/my-alias", "", true, "resource must start with 'key/'"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			keyId, err := ParseAliCloudKmsKeyId(tt.arnStr)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
			assert.Equal(t, tt.wantKeyId, keyId)
		})
	}
}

func TestValidateAliCloudRoleARN(t *testing.T) {
	tests := []struct {
		name      string
		arnStr    string
		wantErr   bool
		errSubstr string
	}{
		{"valid role ARN", "acs:ram::123456789:role/test-role", false, ""},
		{"valid with region", "acs:ram:cn-hangzhou:123456789:role/my-role", false, ""},
		{"empty ARN", "", true, "empty"},
		{"wrong service", "acs:kms:cn-hangzhou:123456789:key/test", true, "service must be 'ram'"},
		{"wrong resource", "acs:ram::123456789:user/test-user", true, "resource must start with 'role/'"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateAliCloudRoleARN(tt.arnStr)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
		})
	}
}

func TestParseGCPKmsKeyName(t *testing.T) {
	tests := []struct {
		name      string
		keyName   string
		wantKey   *GCPKmsKeyName
		wantErr   bool
		errSubstr string
	}{
		{
			name:    "valid key name",
			keyName: "projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key",
			wantKey: &GCPKmsKeyName{
				Project:  "my-project",
				Location: "us-central1",
				KeyRing:  "my-ring",
				KeyId:    "my-key",
			},
		},
		{"empty", "", nil, true, "empty"},
		{"invalid format", "invalid", nil, true, "invalid GCP KMS key name format"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			parsed, err := ParseGCPKmsKeyName(tt.keyName)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
			assert.Equal(t, tt.wantKey.Project, parsed.Project)
			assert.Equal(t, tt.wantKey.Location, parsed.Location)
			assert.Equal(t, tt.wantKey.KeyRing, parsed.KeyRing)
			assert.Equal(t, tt.wantKey.KeyId, parsed.KeyId)
		})
	}
}

func TestParseGCPKmsKeyId(t *testing.T) {
	tests := []struct {
		name      string
		keyName   string
		wantKeyId string
		wantErr   bool
		errSubstr string
	}{
		{"valid", "projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key", "my-key", false, ""},
		{"empty", "", "", true, "empty"},
		{"invalid format", "invalid", "", true, "invalid GCP KMS key name format"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			keyId, err := ParseGCPKmsKeyId(tt.keyName)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
			assert.Equal(t, tt.wantKeyId, keyId)
		})
	}
}

func TestParseAzureKeyVaultKeyURL(t *testing.T) {
	tests := []struct {
		name      string
		keyUrl    string
		wantKey   *AzureKeyVaultKey
		wantErr   bool
		errSubstr string
	}{
		{
			name:   "valid with version",
			keyUrl: "https://my-vault.vault.azure.net/keys/my-key/abc123",
			wantKey: &AzureKeyVaultKey{
				VaultName: "my-vault",
				KeyName:   "my-key",
				Version:   "abc123",
			},
		},
		{
			name:   "valid without version",
			keyUrl: "https://my-vault.vault.azure.net/keys/my-key",
			wantKey: &AzureKeyVaultKey{
				VaultName: "my-vault",
				KeyName:   "my-key",
				Version:   "",
			},
		},
		{"empty", "", nil, true, "empty"},
		{"invalid format", "invalid", nil, true, "invalid Azure Key Vault URL format"},
		{"wrong domain", "https://my-vault.keyvault.azure.com/keys/my-key", nil, true, "invalid Azure Key Vault URL format"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			parsed, err := ParseAzureKeyVaultKeyURL(tt.keyUrl)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
			assert.Equal(t, tt.wantKey.VaultName, parsed.VaultName)
			assert.Equal(t, tt.wantKey.KeyName, parsed.KeyName)
			assert.Equal(t, tt.wantKey.Version, parsed.Version)
		})
	}
}

func TestParseAzureKeyVaultKeyId(t *testing.T) {
	tests := []struct {
		name      string
		keyUrl    string
		wantKeyId string
		wantErr   bool
		errSubstr string
	}{
		{"valid with version", "https://my-vault.vault.azure.net/keys/my-key/abc123", "my-key", false, ""},
		{"valid without version", "https://my-vault.vault.azure.net/keys/my-key", "my-key", false, ""},
		{"empty", "", "", true, "empty"},
		{"invalid format", "invalid", "", true, "invalid Azure Key Vault URL format"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			keyId, err := ParseAzureKeyVaultKeyId(tt.keyUrl)
			if tt.wantErr {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.errSubstr)
				return
			}
			require.NoError(t, err)
			assert.Equal(t, tt.wantKeyId, keyId)
		})
	}
}
