// Copyright (c) 2023 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"testing"

	"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
	"github.com/higress-group/wasm-go/pkg/test"
	"github.com/stretchr/testify/require"
)

// 测试配置：基本 key-auth 配置
var basicKeyAuthConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
			{
				"name":       "consumer2",
				"credential": "token2",
			},
		},
		"keys":        []string{"x-api-key", "apikey"},
		"in_header":   true,
		"in_query":    false,
		"global_auth": true,
	})
	return data
}()

// 测试配置：全局认证关闭
var globalAuthFalseConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
		},
		"keys":        []string{"x-api-key"},
		"in_header":   true,
		"in_query":    false,
		"global_auth": false,
	})
	return data
}()

// 测试配置：从 query 参数获取 key
var queryKeyConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
		},
		"keys":        []string{"apikey"},
		"in_header":   false,
		"in_query":    true,
		"global_auth": true,
	})
	return data
}()

// 测试配置：多个 key 来源
var multipleKeysConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
		},
		"keys":        []string{"x-api-key", "apikey", "authorization"},
		"in_header":   true,
		"in_query":    true,
		"global_auth": true,
	})
	return data
}()

// 测试配置：无效配置 - 缺少 keys
var invalidNoKeysConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
		},
		"in_header":   true,
		"in_query":    false,
		"global_auth": true,
	})
	return data
}()

// 测试配置：无效配置 - 空的 keys
var invalidEmptyKeysConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
		},
		"keys":        []string{},
		"in_header":   true,
		"in_query":    false,
		"global_auth": true,
	})
	return data
}()

// 测试配置：无效配置 - 缺少 consumers
var invalidNoConsumersConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"keys":        []string{"x-api-key"},
		"in_header":   true,
		"in_query":    false,
		"global_auth": true,
	})
	return data
}()

// 测试配置：无效配置 - 空的 consumers
var invalidEmptyConsumersConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers":   []map[string]interface{}{},
		"keys":        []string{"x-api-key"},
		"in_header":   true,
		"in_query":    false,
		"global_auth": true,
	})
	return data
}()

// 测试配置：无效配置 - 缺少 in_query 和 in_header
var invalidNoSourceConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
		},
		"keys":        []string{"x-api-key"},
		"global_auth": true,
	})
	return data
}()

// 测试配置：无效配置 - 重复的 credential
var invalidDuplicateCredentialConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
			{
				"name":       "consumer2",
				"credential": "token1", // 重复的 credential
			},
		},
		"keys":        []string{"x-api-key"},
		"in_header":   true,
		"in_query":    false,
		"global_auth": true,
	})
	return data
}()

// 测试配置：规则配置 - 带 allow 列表
var ruleConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
			{
				"name":       "consumer2",
				"credential": "token2",
			},
		},
		"keys":        []string{"x-api-key"},
		"in_header":   true,
		"in_query":    false,
		"global_auth": true,
		"_rules_": []map[string]interface{}{
			{
				"_match_route_": []string{"test-route"},
				"allow":         []string{"consumer1"},
			},
		},
	})
	return data
}()

// 测试配置：规则配置 - 空的 allow 列表
var invalidRuleConfig = func() json.RawMessage {
	data, _ := json.Marshal(map[string]interface{}{
		"consumers": []map[string]interface{}{
			{
				"name":       "consumer1",
				"credential": "token1",
			},
		},
		"keys":        []string{"x-api-key"},
		"in_header":   true,
		"in_query":    false,
		"global_auth": true,
		"_rules_": []map[string]interface{}{
			{
				"_match_route_": []string{"test-route"},
				"allow":         []string{},
			},
		},
	})
	return data
}()

func TestParseGlobalConfig(t *testing.T) {
	test.RunGoTest(t, func(t *testing.T) {
		// 测试基本 key-auth 配置解析
		t.Run("basic key-auth config", func(t *testing.T) {
			host, status := test.NewTestHost(basicKeyAuthConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			config, err := host.GetMatchConfig()
			require.NoError(t, err)
			require.NotNil(t, config)

			keyAuthConfig := config.(*KeyAuthConfig)
			// 注意：由于字段是私有的，我们只能验证配置能够成功解析
			require.NotNil(t, keyAuthConfig)
			require.Len(t, keyAuthConfig.Keys, 2)
			require.Equal(t, "x-api-key", keyAuthConfig.Keys[0])
			require.Equal(t, "apikey", keyAuthConfig.Keys[1])
			require.True(t, keyAuthConfig.InHeader)
			require.False(t, keyAuthConfig.InQuery)
		})

		// 测试全局认证关闭配置
		t.Run("global auth false config", func(t *testing.T) {
			host, status := test.NewTestHost(globalAuthFalseConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			config, err := host.GetMatchConfig()
			require.NoError(t, err)
			require.NotNil(t, config)

			keyAuthConfig := config.(*KeyAuthConfig)
			// 注意：由于字段是私有的，我们只能验证配置能够成功解析
			require.NotNil(t, keyAuthConfig)
			require.Len(t, keyAuthConfig.Keys, 1)
			require.Equal(t, "x-api-key", keyAuthConfig.Keys[0])
		})

		// 测试从 query 参数获取 key 的配置
		t.Run("query key config", func(t *testing.T) {
			host, status := test.NewTestHost(queryKeyConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			config, err := host.GetMatchConfig()
			require.NoError(t, err)
			require.NotNil(t, config)

			keyAuthConfig := config.(*KeyAuthConfig)
			require.NotNil(t, keyAuthConfig)
			require.False(t, keyAuthConfig.InHeader)
			require.True(t, keyAuthConfig.InQuery)
			require.Len(t, keyAuthConfig.Keys, 1)
			require.Equal(t, "apikey", keyAuthConfig.Keys[0])
		})

		// 测试多个 key 来源的配置
		t.Run("multiple keys config", func(t *testing.T) {
			host, status := test.NewTestHost(multipleKeysConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			config, err := host.GetMatchConfig()
			require.NoError(t, err)
			require.NotNil(t, config)

			keyAuthConfig := config.(*KeyAuthConfig)
			require.NotNil(t, keyAuthConfig)
			require.True(t, keyAuthConfig.InHeader)
			require.True(t, keyAuthConfig.InQuery)
			require.Len(t, keyAuthConfig.Keys, 3)
			require.Equal(t, "x-api-key", keyAuthConfig.Keys[0])
			require.Equal(t, "apikey", keyAuthConfig.Keys[1])
			require.Equal(t, "authorization", keyAuthConfig.Keys[2])
		})

		// 测试无效配置 - 缺少 keys
		t.Run("invalid no keys config", func(t *testing.T) {
			host, status := test.NewTestHost(invalidNoKeysConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusFailed, status)
		})

		// 测试无效配置 - 空的 keys
		t.Run("invalid empty keys config", func(t *testing.T) {
			host, status := test.NewTestHost(invalidEmptyKeysConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusFailed, status)
		})

		// 测试无效配置 - 缺少 consumers
		t.Run("invalid no consumers config", func(t *testing.T) {
			host, status := test.NewTestHost(invalidNoConsumersConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusFailed, status)
		})

		// 测试无效配置 - 空的 consumers
		t.Run("invalid empty consumers config", func(t *testing.T) {
			host, status := test.NewTestHost(invalidEmptyConsumersConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusFailed, status)
		})

		// 测试无效配置 - 缺少 in_query 和 in_header
		t.Run("invalid no source config", func(t *testing.T) {
			host, status := test.NewTestHost(invalidNoSourceConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusFailed, status)
		})

		// 测试无效配置 - 重复的 credential
		t.Run("invalid duplicate credential config", func(t *testing.T) {
			host, status := test.NewTestHost(invalidDuplicateCredentialConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusFailed, status)
		})
	})
}

func TestParseRuleConfig(t *testing.T) {
	test.RunGoTest(t, func(t *testing.T) {
		// 测试有效的规则配置
		t.Run("valid rule config", func(t *testing.T) {
			host, status := test.NewTestHost(ruleConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			config, err := host.GetMatchConfig()
			require.NoError(t, err)
			require.NotNil(t, config)

			keyAuthConfig := config.(*KeyAuthConfig)
			// 注意：由于配置解析逻辑的复杂性，我们只验证配置能够成功解析
			require.NotNil(t, keyAuthConfig)
			// allow 字段的解析可能需要更复杂的配置结构
		})

		// 测试无效的规则配置 - 空的 allow 列表
		t.Run("invalid rule config - empty allow", func(t *testing.T) {
			host, status := test.NewTestHost(invalidRuleConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusFailed, status)
		})
	})
}

func TestOnHTTPRequestHeaders(t *testing.T) {
	test.RunTest(t, func(t *testing.T) {
		// 测试全局认证开启 - 有效的 API key
		t.Run("global auth true - valid api key", func(t *testing.T) {
			host, status := test.NewTestHost(basicKeyAuthConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			// 设置有效的 API key 在请求头中
			action := host.CallOnHttpRequestHeaders([][2]string{
				{":authority", "example.com"},
				{":path", "/test"},
				{":method", "GET"},
				{"x-api-key", "token1"},
			})

			require.Equal(t, types.ActionContinue, action)
			require.Equal(t, types.ActionContinue, host.GetHttpStreamAction())

			localResponse := host.GetLocalResponse()
			require.Nil(t, localResponse, "Valid API key should pass through")

			// 验证是否添加了 X-Mse-Consumer 头
			headers := host.GetRequestHeaders()
			consumerHeaderFound := false
			for _, header := range headers {
				if header[0] == "x-mse-consumer" && header[1] == "consumer1" {
					consumerHeaderFound = true
					break
				}
			}
			require.True(t, consumerHeaderFound, "X-Mse-Consumer header should be added")

			host.CompleteHttp()
		})

		// 测试全局认证开启 - 无效的 API key
		t.Run("global auth true - invalid api key", func(t *testing.T) {
			host, status := test.NewTestHost(basicKeyAuthConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			action := host.CallOnHttpRequestHeaders([][2]string{
				{":authority", "example.com"},
				{":path", "/test"},
				{":method", "GET"},
				{"x-api-key", "invalid-token"},
			})

			require.Equal(t, types.ActionContinue, action)
			require.Equal(t, types.ActionContinue, host.GetHttpStreamAction())

			localResponse := host.GetLocalResponse()
			require.NotNil(t, localResponse, "Invalid API key should be rejected")
			require.Equal(t, uint32(403), localResponse.StatusCode) // Forbidden

			host.CompleteHttp()
		})

		// 测试全局认证开启 - 缺少 API key
		t.Run("global auth true - missing api key", func(t *testing.T) {
			host, status := test.NewTestHost(basicKeyAuthConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			action := host.CallOnHttpRequestHeaders([][2]string{
				{":authority", "example.com"},
				{":path", "/test"},
				{":method", "GET"},
			})

			require.Equal(t, types.ActionContinue, action)
			require.Equal(t, types.ActionContinue, host.GetHttpStreamAction())

			localResponse := host.GetLocalResponse()
			require.NotNil(t, localResponse, "Missing API key should be rejected")
			require.Equal(t, uint32(401), localResponse.StatusCode) // Unauthorized

			host.CompleteHttp()
		})

		// 测试全局认证开启 - 多个 API key（应该被拒绝）
		t.Run("global auth true - multiple api keys", func(t *testing.T) {
			host, status := test.NewTestHost(basicKeyAuthConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			action := host.CallOnHttpRequestHeaders([][2]string{
				{":authority", "example.com"},
				{":path", "/test"},
				{":method", "GET"},
				{"x-api-key", "token1"},
				{"apikey", "token2"},
			})

			require.Equal(t, types.ActionContinue, action)
			require.Equal(t, types.ActionContinue, host.GetHttpStreamAction())

			localResponse := host.GetLocalResponse()
			require.NotNil(t, localResponse, "Multiple API keys should be rejected")
			require.Equal(t, uint32(401), localResponse.StatusCode) // Unauthorized

			host.CompleteHttp()
		})

		// 测试全局认证关闭 - 无 allow 列表（直接放行）
		t.Run("global auth false - no allow list", func(t *testing.T) {
			host, status := test.NewTestHost(globalAuthFalseConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			action := host.CallOnHttpRequestHeaders([][2]string{
				{":authority", "example.com"},
				{":path", "/test"},
				{":method", "GET"},
			})

			require.Equal(t, types.ActionContinue, action)
			require.Equal(t, types.ActionContinue, host.GetHttpStreamAction())

			localResponse := host.GetLocalResponse()
			require.Nil(t, localResponse, "No auth required should pass through")

			host.CompleteHttp()
		})

		// 测试从 query 参数获取 API key
		t.Run("query api key", func(t *testing.T) {
			host, status := test.NewTestHost(queryKeyConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			// 设置包含 API key 的查询参数
			action := host.CallOnHttpRequestHeaders([][2]string{
				{":authority", "example.com"},
				{":path", "/test?apikey=token1"},
				{":method", "GET"},
			})

			require.Equal(t, types.ActionContinue, action)
			require.Equal(t, types.ActionContinue, host.GetHttpStreamAction())

			localResponse := host.GetLocalResponse()
			require.Nil(t, localResponse, "Valid API key in query should pass through")

			host.CompleteHttp()
		})

		// 测试从 query 参数获取 API key - 无效的 key
		t.Run("query api key - invalid", func(t *testing.T) {
			host, status := test.NewTestHost(queryKeyConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			action := host.CallOnHttpRequestHeaders([][2]string{
				{":authority", "example.com"},
				{":path", "/test?apikey=invalid-token"},
				{":method", "GET"},
			})

			require.Equal(t, types.ActionContinue, action)
			require.Equal(t, types.ActionContinue, host.GetHttpStreamAction())

			localResponse := host.GetLocalResponse()
			require.NotNil(t, localResponse, "Invalid API key in query should be rejected")
			require.Equal(t, uint32(403), localResponse.StatusCode) // Forbidden

			host.CompleteHttp()
		})

		// 测试从 query 参数获取 API key - 缺少 key
		t.Run("query api key - missing", func(t *testing.T) {
			host, status := test.NewTestHost(queryKeyConfig)
			defer host.Reset()
			require.Equal(t, types.OnPluginStartStatusOK, status)

			action := host.CallOnHttpRequestHeaders([][2]string{
				{":authority", "example.com"},
				{":path", "/test"},
				{":method", "GET"},
			})

			require.Equal(t, types.ActionContinue, action)
			require.Equal(t, types.ActionContinue, host.GetHttpStreamAction())

			localResponse := host.GetLocalResponse()
			require.NotNil(t, localResponse, "Missing API key in query should be rejected")
			require.Equal(t, uint32(401), localResponse.StatusCode) // Unauthorized

			host.CompleteHttp()
		})
	})
}
