/*
   Copyright The containerd Authors.

   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 infoutil

import (
	"testing"

	"golang.org/x/sys/windows"
	"golang.org/x/sys/windows/registry"
	"gotest.tools/v3/assert"

	mocks "github.com/containerd/nerdctl/v2/pkg/infoutil/infoutilmock"
)

func newRegistryFake() *mocks.FakeWindowsInfoUtil {
	f := mocks.NewFakeWindowsInfoUtil()
	f.GetRegistryStringValueFunc = func(_ registry.Key, _ string, name string) (string, error) {
		switch name {
		case "CurrentBuildNumber":
			return "19041", nil
		case "DisplayVersion":
			return "22H4", nil
		}
		return "", nil
	}
	f.GetRegistryIntValueFunc = func(_ registry.Key, _ string, name string) (int, error) {
		if name == "UBR" {
			return 558, nil
		}
		return 0, nil
	}
	return f
}

const (
	verNTWorkStation      = 0x0000001
	verNTDomainController = 0x0000002
)

func TestDistroName(t *testing.T) {
	baseVersion := windows.OsVersionInfoEx{
		MajorVersion: 10,
		MinorVersion: 0,
		BuildNumber:  19041,
	}

	tests := []struct {
		productType byte
		expected    string
	}{
		{
			productType: verNTWorkStation,
			expected:    "Microsoft Windows Version 22H4 (OS Build 19041.558)",
		},
		{
			productType: verNTServer,
			expected:    "Microsoft Windows Server Version 22H4 (OS Build 19041.558)",
		},
	}

	for _, tt := range tests {
		fake := newRegistryFake()
		osvi := baseVersion
		osvi.ProductType = tt.productType
		fake.RtlGetVersionFunc = func() *windows.OsVersionInfoEx { return &osvi }

		t.Run(tt.expected, func(t *testing.T) {
			actual, err := distroName(fake)
			assert.Equal(t, tt.expected, actual, "distroName should return the name of the operating system")
			assert.NilError(t, err)
		})
	}
}

func TestDistroNameError(t *testing.T) {
	fake := mocks.NewFakeWindowsInfoUtil()
	fake.GetRegistryStringValueFunc = func(_ registry.Key, _ string, _ string) (string, error) {
		return "19041", registry.ErrNotExist
	}

	actual, err := distroName(fake)
	assert.ErrorContains(t, err, registry.ErrNotExist.Error(), "distroName should return an error on error")
	assert.Equal(t, "", actual, "distroname should return an empty string on error")
}

func TestGetKernelVersion(t *testing.T) {
	fake := mocks.NewFakeWindowsInfoUtil()
	fake.GetRegistryStringValueFunc = func(_ registry.Key, _ string, name string) (string, error) {
		if name == "BuildLabEx" {
			return "10240.16412.amd64fre.th1.150729-1800", nil
		}
		return "", nil
	}

	baseVersion := windows.OsVersionInfoEx{
		MajorVersion: 10,
		MinorVersion: 0,
		BuildNumber:  19041,
	}
	fake.RtlGetVersionFunc = func() *windows.OsVersionInfoEx {
		v := baseVersion
		return &v
	}

	expected := "10.0 19041 (10240.16412.amd64fre.th1.150729-1800)"

	actual, err := getKernelVersion(fake)
	assert.NilError(t, err)
	assert.Equal(t, expected, actual, "getKernelVersion should return the kernel version")
}

func TestGetKernelVersionError(t *testing.T) {
	fake := mocks.NewFakeWindowsInfoUtil()
	fake.GetRegistryStringValueFunc = func(_ registry.Key, _ string, _ string) (string, error) {
		return "", registry.ErrNotExist
	}

	actual, err := getKernelVersion(fake)
	assert.ErrorContains(t, err, registry.ErrNotExist.Error(), "getKernelVersion should return an error on error")
	assert.Equal(t, "", actual, "getKernelVersion should return an empty string on error")
}

func TestIsWindowsServer(t *testing.T) {
	tests := []struct {
		productType string
		osvi        windows.OsVersionInfoEx
		expected    bool
	}{
		{
			productType: "VER_NT_WORKSTATION",
			osvi:        windows.OsVersionInfoEx{ProductType: verNTWorkStation},
			expected:    false,
		},
		{
			productType: "VER_NT_DOMAIN_CONTROLLER",
			osvi:        windows.OsVersionInfoEx{ProductType: verNTDomainController},
			expected:    false,
		},
		{
			productType: "VER_NT_SERVER",
			osvi:        windows.OsVersionInfoEx{ProductType: verNTServer},
			expected:    true,
		},
	}

	for _, tt := range tests {
		tt := tt
		fake := mocks.NewFakeWindowsInfoUtil()
		fake.RtlGetVersionFunc = func() *windows.OsVersionInfoEx { return &tt.osvi }

		t.Run(tt.productType, func(t *testing.T) {
			actual := isWindowsServer(fake)
			assert.Equal(t, tt.expected, actual, "isWindowsServer should return true on Windows Server")
		})
	}
}
