// Copyright IBM Corp. 2016, 2025
// SPDX-License-Identifier: BUSL-1.1

package configutil

import (
	"reflect"
	"testing"
	"time"

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

// TestMerge tests SharedConfig#Merge
func TestMerge(t *testing.T) {
	tests := []struct {
		name   string
		c1     *SharedConfig
		c2     *SharedConfig
		expect *SharedConfig
	}{
		{
			"nil second config",
			&SharedConfig{},
			nil,
			&SharedConfig{},
		},
		{
			"blank configs",
			&SharedConfig{},
			&SharedConfig{},
			&SharedConfig{},
		},
		{
			"combined FoundKeys",
			&SharedConfig{
				FoundKeys: []string{
					"DisableMlock",
					"ClusterName",
				},
			},
			&SharedConfig{
				FoundKeys: []string{
					"PidFile",
					"DefaultMaxRequestDuration",
				},
			},
			&SharedConfig{
				FoundKeys: []string{
					"DisableMlock",
					"ClusterName",
					"PidFile",
					"DefaultMaxRequestDuration",
				},
			},
		},
		{
			"Entropy Overwrite",
			&SharedConfig{
				Entropy: &Entropy{
					Mode:     EntropyAugmentation,
					SealName: "seal-to-be-overwritten",
				},
			},
			&SharedConfig{
				Entropy: &Entropy{
					Mode:     EntropyAugmentation,
					SealName: "new-seal",
				},
			},
			&SharedConfig{
				Entropy: &Entropy{
					Mode:     EntropyAugmentation,
					SealName: "new-seal",
				},
			},
		},
		{
			"DisableMlock true overrides false",
			&SharedConfig{
				DisableMlock: false,
			},
			&SharedConfig{
				DisableMlock: true,
			},
			&SharedConfig{
				DisableMlock: true,
			},
		},
		{
			"longer duration overrides shorter",
			&SharedConfig{
				DefaultMaxRequestDuration: time.Duration(5 * time.Second),
			},
			&SharedConfig{
				DefaultMaxRequestDuration: time.Duration(10 * time.Second),
			},
			&SharedConfig{
				DefaultMaxRequestDuration: time.Duration(10 * time.Second),
			},
		},
		{
			"combined listeners",
			&SharedConfig{
				Listeners: []*Listener{
					{
						Type:    TCP,
						Address: "127.0.0.1",
					},
					{
						Type:    Unix,
						Address: "mnt/listener",
					},
				},
			},
			&SharedConfig{
				Listeners: []*Listener{
					{
						Type:    TCP,
						Address: "127.0.0.3",
					},
					{
						Type:    Unix,
						Address: "mnt/listener2",
					},
				},
			},
			&SharedConfig{
				Listeners: []*Listener{
					{
						Type:    TCP,
						Address: "127.0.0.1",
					},
					{
						Type:    Unix,
						Address: "mnt/listener",
					},
					{
						Type:    TCP,
						Address: "127.0.0.3",
					},
					{
						Type:    Unix,
						Address: "mnt/listener2",
					},
				},
			},
		},
		{
			"combined user lockouts",
			&SharedConfig{
				UserLockouts: []*UserLockout{
					{
						Type: "lockout1",
					},
				},
			},
			&SharedConfig{
				UserLockouts: []*UserLockout{
					{
						Type: "lockout2",
					},
				},
			},
			&SharedConfig{
				UserLockouts: []*UserLockout{
					{
						Type: "lockout1",
					},
					{
						Type: "lockout2",
					},
				},
			},
		},
		{
			"combined seals",
			&SharedConfig{
				Seals: []*KMS{
					{
						Purpose: []string{"purpose1"},
					},
				},
			},
			&SharedConfig{
				Seals: []*KMS{
					{
						Purpose: []string{"purpose2"},
					},
				},
			},
			&SharedConfig{
				Seals: []*KMS{
					{
						Purpose: []string{"purpose1"},
					},
					{
						Purpose: []string{"purpose2"},
					},
				},
			},
		},
		{
			"telemetry overwrite",
			&SharedConfig{
				Telemetry: &Telemetry{
					StatsiteAddr: "https://example.com",
				},
			},
			&SharedConfig{
				Telemetry: &Telemetry{},
			},
			&SharedConfig{
				Telemetry: &Telemetry{},
			},
		},
		{
			"HCPLinkConf overwrite",
			&SharedConfig{},
			&SharedConfig{},
			&SharedConfig{},
		},
		{
			"log fields overwrite",
			&SharedConfig{
				LogFile:           "file1.log",
				LogFormat:         "json",
				LogLevel:          "warn",
				LogRotateBytes:    2048,
				LogRotateDuration: "24h",
				LogRotateMaxFiles: 32,
			},
			&SharedConfig{
				LogFile:              "file2.log",
				LogFormat:            "txt",
				LogLevel:             "error",
				LogRotateBytes:       1024,
				LogRotateBytesRaw:    1024,
				LogRotateDuration:    "12h",
				LogRotateMaxFiles:    8,
				LogRotateMaxFilesRaw: 8,
			},
			&SharedConfig{
				LogFile:              "file2.log",
				LogFormat:            "txt",
				LogLevel:             "error",
				LogRotateBytes:       1024,
				LogRotateBytesRaw:    1024,
				LogRotateDuration:    "12h",
				LogRotateMaxFiles:    8,
				LogRotateMaxFilesRaw: 8,
			},
		},
		{
			"log fields raw overwrite",
			&SharedConfig{
				LogFile:           "file1.log",
				LogFormat:         "json",
				LogLevel:          "warn",
				LogRotateBytes:    2048,
				LogRotateDuration: "24h",
				LogRotateMaxFiles: 32,
			},
			&SharedConfig{
				LogRotateBytes:       1024,
				LogRotateBytesRaw:    1024,
				LogRotateMaxFiles:    8,
				LogRotateMaxFilesRaw: 8,
			},
			&SharedConfig{
				LogFile:              "file1.log",
				LogFormat:            "json",
				LogLevel:             "warn",
				LogRotateDuration:    "24h",
				LogRotateBytes:       1024,
				LogRotateBytesRaw:    1024,
				LogRotateMaxFiles:    8,
				LogRotateMaxFilesRaw: 8,
			},
		},
		{
			"pidfile overwrite",
			&SharedConfig{
				PidFile: "file1.pid",
			},
			&SharedConfig{
				PidFile: "file2.pid",
			},
			&SharedConfig{
				PidFile: "file2.pid",
			},
		},
		{
			"cluster name overwrite",
			&SharedConfig{
				ClusterName: "vault-cluster1",
			},
			&SharedConfig{
				ClusterName: "vault-cluster2",
			},
			&SharedConfig{
				ClusterName: "vault-cluster2",
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			require.True(t, reflect.DeepEqual(tc.expect, tc.c1.Merge(tc.c2)))
		})
	}
}
