/*
 * Copyright (c) 2025 Bocloud Technologies Co., Ltd.
 * installer is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package bkeconsole

import (
	"embed"
	"fmt"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/agiledragon/gomonkey/v2"
	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	fake "k8s.io/client-go/kubernetes/fake"

	"gopkg.openfuyao.cn/bkeadm/pkg/common/types"
	econd "gopkg.openfuyao.cn/bkeadm/pkg/executor/containerd"
	"gopkg.openfuyao.cn/bkeadm/pkg/executor/exec"
	"gopkg.openfuyao.cn/bkeadm/pkg/executor/k8s"
	"gopkg.openfuyao.cn/bkeadm/pkg/global"
	"gopkg.openfuyao.cn/bkeadm/utils"
	"gopkg.openfuyao.cn/bkeadm/utils/log"
)

const (
	testNumericZero      = 0
	testNumericOne       = 1
	testNumericTwo       = 2
	testNumericThree     = 3
	testNumericFive      = 5
	testNumericTen       = 10
	testFilePerm         = 0644
	testFileModeReadOnly = 0644
	testFileModeExec     = 0755
)

const (
	testIPv4SegA = 192
	testIPv4SegB = 168
	testIPv4SegC = 1
	testIPv4SegD = 100
)

const (
	testIPv4SegA2 = 8
	testIPv4SegB2 = 8
	testIPv4SegC2 = 8
	testIPv4SegD2 = 8
)

const (
	testIPv4SegA3 = 114
	testIPv4SegB3 = 114
	testIPv4SegC3 = 114
	testIPv4SegD3 = 114
)

const (
	testIPv4SegA4 = 192
	testIPv4SegB4 = 168
	testIPv4SegC4 = 1
	testIPv4SegD4 = 200
)

const (
	testIPv4SegA5 = 127
	testIPv4SegB5 = 0
	testIPv4SegC5 = 0
	testIPv4SegD5 = 1
)

var (
	testIPAddr       = fmt.Sprintf("%d.%d.%d.%d", testIPv4SegA, testIPv4SegB, testIPv4SegC, testIPv4SegD)
	testDNSPrimary   = fmt.Sprintf("%d.%d.%d.%d", testIPv4SegA2, testIPv4SegB2, testIPv4SegC2, testIPv4SegD2)
	testDNSSecondary = fmt.Sprintf("%d.%d.%d.%d", testIPv4SegA3, testIPv4SegB3, testIPv4SegC3, testIPv4SegD3)
	testOtherRepoIP  = fmt.Sprintf("%d.%d.%d.%d", testIPv4SegA4, testIPv4SegB4, testIPv4SegC4, testIPv4SegD4)
	testLoopbackIP   = fmt.Sprintf("%d.%d.%d.%d", testIPv4SegA5, testIPv4SegB5, testIPv4SegC5, testIPv4SegD5)
)

func TestWriteToDir(t *testing.T) {
	tempDir := t.TempDir()

	tests := []struct {
		name           string
		dir            string
		script         string
		scriptFile     string
		mockExists     func(string) bool
		mockMkdirAll   func(string, os.FileMode) error
		mockWriteFile  func(string, []byte, os.FileMode) error
		expectError    bool
		expectedErrMsg string
	}{
		{
			name:       "successful write to new directory",
			dir:        filepath.Join(tempDir, "new-dir"),
			script:     "test.sh",
			scriptFile: "#!/bin/bash\necho test",
			mockExists: func(path string) bool { return false },
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return nil
			},
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return nil
			},
			expectError:    false,
			expectedErrMsg: "",
		},
		{
			name:       "successful write to existing directory",
			dir:        tempDir,
			script:     "test.sh",
			scriptFile: "#!/bin/bash\necho test",
			mockExists: func(path string) bool { return true },
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return nil
			},
			expectError:    false,
			expectedErrMsg: "",
		},
		{
			name:       "mkdir all fails",
			dir:        filepath.Join(tempDir, "new-dir"),
			script:     "test.sh",
			scriptFile: "#!/bin/bash\necho test",
			mockExists: func(path string) bool { return false },
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return fmt.Errorf("mkdir error")
			},
			expectError:    true,
			expectedErrMsg: "create dir failed",
		},
		{
			name:       "write file fails",
			dir:        tempDir,
			script:     "test.sh",
			scriptFile: "#!/bin/bash\necho test",
			mockExists: func(path string) bool { return true },
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return fmt.Errorf("write error")
			},
			expectError:    true,
			expectedErrMsg: "write test.sh fialed",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patches := gomonkey.NewPatches()
			defer patches.Reset()

			patches.ApplyFunc(utils.Exists, tt.mockExists)
			if tt.mockMkdirAll != nil {
				patches.ApplyFunc(os.MkdirAll, tt.mockMkdirAll)
			}
			if tt.mockWriteFile != nil {
				patches.ApplyFunc(os.WriteFile, tt.mockWriteFile)
			}

			err := writeToDir(tt.dir, tt.script, tt.scriptFile)

			if tt.expectError {
				assert.Error(t, err)
				if tt.expectedErrMsg != "" {
					assert.Contains(t, err.Error(), tt.expectedErrMsg)
				}
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestDeployConsole(t *testing.T) {
	tests := []struct {
		name             string
		otherRepo        string
		onlineImage      string
		hostIP           string
		repo             string
		openFuyaoVersion string
		mockExists       func(string) bool
		mockMkdirAll     func(string, os.FileMode) error
		mockCopyFS       func(embed.FS, string, string) error
		mockWriteDir     func(string, string, string) error
		mockExecuteCmd   func(*exec.CommandExecutor, string, ...string) (string, error)
		expectError      bool
	}{
		{
			name:             "mkdir resource dir fails",
			otherRepo:        "",
			onlineImage:      "",
			hostIP:           testIPAddr,
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockExists: func(path string) bool {
				return false
			},
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return fmt.Errorf("mkdir error")
			},
			expectError: true,
		},
		{
			name:             "execute command fails",
			otherRepo:        "",
			onlineImage:      "",
			hostIP:           testIPAddr,
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockExists: func(path string) bool {
				return true
			},
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return nil
			},
			mockCopyFS: func(efs embed.FS, src string, dst string) error {
				return nil
			},
			mockWriteDir: func(dir string, script string, scriptContent string) error {
				return nil
			},
			mockExecuteCmd: func(c *exec.CommandExecutor, command string, args ...string) (string, error) {
				return "", fmt.Errorf("command failed")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(utils.Exists, tt.mockExists)

			patches.ApplyFunc(os.MkdirAll, tt.mockMkdirAll)

			if tt.mockCopyFS != nil {
				patches.ApplyFunc(copyEmbeddedFS, tt.mockCopyFS)
			}

			if tt.mockWriteDir != nil {
				patches.ApplyFunc(writeToDir, tt.mockWriteDir)
			}

			patches.ApplyFunc((*exec.CommandExecutor).ExecuteCommandWithCombinedOutput, tt.mockExecuteCmd)

			err := deployConsole(tt.otherRepo, tt.onlineImage, tt.hostIP, tt.repo, tt.openFuyaoVersion)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestGenerateSecret(t *testing.T) {
	tests := []struct {
		name           string
		mockWriteFile  func(string, []byte, os.FileMode) error
		mockExecuteCmd func(*exec.CommandExecutor, string, ...string) (string, error)
		expectError    bool
	}{
		{
			name: "write file fails",
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return fmt.Errorf("write error")
			},
			expectError: true,
		},
		{
			name: "execute command fails",
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return nil
			},
			mockExecuteCmd: func(c *exec.CommandExecutor, command string, args ...string) (string, error) {
				return "", fmt.Errorf("command failed")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(writeToDir, func(dir string, script string, scriptContent string) error {
				return tt.mockWriteFile(filepath.Join(dir, script), []byte(scriptContent), utils.DefaultFilePermission)
			})

			patches.ApplyFunc((*exec.CommandExecutor).ExecuteCommandWithCombinedOutput, tt.mockExecuteCmd)

			err := generateSecret()

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestGetDNSServers(t *testing.T) {
	tests := []struct {
		name        string
		dnsConfig   string
		expectError bool
		expectLen   int
	}{
		{
			name:        "valid dns config",
			dnsConfig:   "servers:\n  - " + testDNSPrimary + "\n  - " + testDNSSecondary + "\n",
			expectError: false,
			expectLen:   2,
		},
		{
			name:        "empty dns config",
			dnsConfig:   "servers: []\n",
			expectError: true,
			expectLen:   0,
		},
		{
			name:        "invalid yaml",
			dnsConfig:   "invalid: yaml: content:",
			expectError: true,
			expectLen:   0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dnsConfig = tt.dnsConfig
			defer func() {
				dnsConfig = ""
			}()

			servers, err := getDNSServers()

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Len(t, servers, tt.expectLen)
			}
		})
	}
}

func TestLogContainerWaitingStatus(t *testing.T) {
	tests := []struct {
		name         string
		pod          *corev1.Pod
		expectCalled bool
	}{
		{
			name: "pod with waiting container",
			pod: &corev1.Pod{
				Status: corev1.PodStatus{
					ContainerStatuses: []corev1.ContainerStatus{
						{
							State: corev1.ContainerState{
								Waiting: &corev1.ContainerStateWaiting{
									Reason:  "ImagePullBackOff",
									Message: "Unable to pull image",
								},
							},
						},
					},
				},
			},
			expectCalled: true,
		},
		{
			name: "pod with running container",
			pod: &corev1.Pod{
				Status: corev1.PodStatus{
					ContainerStatuses: []corev1.ContainerStatus{
						{
							State: corev1.ContainerState{
								Running: &corev1.ContainerStateRunning{},
							},
						},
					},
				},
			},
			expectCalled: false,
		},
		{
			name:         "pod with no containers",
			pod:          &corev1.Pod{},
			expectCalled: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logCalled := false
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(log.Warnf, func(template string, args ...any) {
				logCalled = true
			})

			logContainerWaitingStatus(tt.pod)
			assert.Equal(t, tt.expectCalled, logCalled)
		})
	}
}

func TestIsPodRunning(t *testing.T) {
	tests := []struct {
		name     string
		pod      *corev1.Pod
		expected bool
	}{
		{
			name: "pod is running",
			pod: &corev1.Pod{
				Status: corev1.PodStatus{
					Phase: corev1.PodRunning,
				},
			},
			expected: true,
		},
		{
			name: "pod is pending",
			pod: &corev1.Pod{
				Status: corev1.PodStatus{
					Phase: corev1.PodPending,
				},
			},
			expected: false,
		},
		{
			name: "pod is succeeded",
			pod: &corev1.Pod{
				Status: corev1.PodStatus{
					Phase: corev1.PodSucceeded,
				},
			},
			expected: false,
		},
		{
			name: "pod is failed",
			pod: &corev1.Pod{
				Status: corev1.PodStatus{
					Phase: corev1.PodFailed,
				},
			},
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := isPodRunning(tt.pod)
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestCheckAllPodsRunning(t *testing.T) {
	tests := []struct {
		name     string
		pods     []corev1.Pod
		expected bool
	}{
		{
			name: "all pods running",
			pods: []corev1.Pod{
				{Status: corev1.PodStatus{Phase: corev1.PodRunning}},
				{Status: corev1.PodStatus{Phase: corev1.PodRunning}},
			},
			expected: true,
		},
		{
			name: "one pod pending",
			pods: []corev1.Pod{
				{Status: corev1.PodStatus{Phase: corev1.PodRunning}},
				{Status: corev1.PodStatus{Phase: corev1.PodPending}},
			},
			expected: false,
		},
		{
			name:     "empty pods",
			pods:     []corev1.Pod{},
			expected: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := checkAllPodsRunning(tt.pods)
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestConstants(t *testing.T) {
	assert.Equal(t, "/var/lib/rancher/k3s/", scriptDir)
	assert.Equal(t, "/var/lib/rancher/k3s/resource", resourceDir)
	assert.Equal(t, "60s", cacheTtl)
	assert.Equal(t, "kubernetes", k3sName)
}

func TestK3sRestartLogic(t *testing.T) {
	config := types.K3sRestartConfig{
		OtherRepo:      "",
		HostIP:         testIPAddr,
		ImageRepo:      "registry.example.com",
		ImageRepoPort:  "5000",
		OtherRepoIp:    "",
		KubernetesPort: "6443",
	}

	tests := []struct {
		name             string
		mockRun          func([]string) error
		mockEnsureImage  func(string) error
		mockContainerErr error
		expectError      bool
	}{
		{
			name: "stop k3s fails",
			mockRun: func(args []string) error {
				return fmt.Errorf("stop error")
			},
			expectError: true,
		},
		{
			name: "ensure image fails",
			mockRun: func(args []string) error {
				return nil
			},
			mockEnsureImage: func(image string) error {
				return fmt.Errorf("image error")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(econd.Run, tt.mockRun)

			patches.ApplyFunc(econd.EnsureImageExists, tt.mockEnsureImage)

			patches.ApplyFunc(econd.ContainerInspect, func(name string) (econd.NerdContainerInfo, error) {
				return econd.NerdContainerInfo{}, tt.mockContainerErr
			})

			patches.ApplyFunc(startK3sContainer, func(a, b, c, d string) error {
				return nil
			})

			patches.ApplyFunc(processKubeconfig, func(a, b string) (string, error) {
				return "/test/.kube/config", nil
			})

			patches.ApplyFunc(waitForKubernetesReady, func(a string) error {
				return nil
			})

			patches.ApplyFunc(waitForClusterReady, func() error {
				return nil
			})

			err := k3sRestart(config)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestStartK3sContainerLogic(t *testing.T) {
	tests := []struct {
		name        string
		mockGetDNS  func() ([]string, error)
		mockRun     func([]string) error
		expectError bool
	}{
		{
			name: "get dns servers fails",
			mockGetDNS: func() ([]string, error) {
				return nil, fmt.Errorf("dns error")
			},
			expectError: true,
		},
		{
			name: "run container fails",
			mockGetDNS: func() ([]string, error) {
				return []string{testDNSPrimary}, nil
			},
			mockRun: func(args []string) error {
				return fmt.Errorf("container error")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(getDNSServers, tt.mockGetDNS)

			patches.ApplyFunc(econd.Run, tt.mockRun)

			err := startK3sContainer(testIPAddr, "registry.example.com", testOtherRepoIP, "6443")

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestProcessKubeconfigLogic(t *testing.T) {
	sleepPatches := gomonkey.ApplyFunc(time.Sleep, func(time.Duration) {})
	defer sleepPatches.Reset()

	kubeconfigContent := fmt.Sprintf(`apiVersion: v1
clusters:
- cluster:
    server: https://%s:36443
   name: default
contexts:
- context:
     cluster: default
     user: default
   name: default
current-context: default
kind: Config
users:
- name: default
   user:
      token: test-token
`, testLoopbackIP)

	tests := []struct {
		name           string
		kubeconfigData []byte
		mockReadFile   func(string) ([]byte, error)
		mockUserHome   func() (string, error)
		mockMkdirAll   func(string, os.FileMode) error
		mockWriteFile  func(string, []byte, os.FileMode) error
		mockRemoveFile func(string) error
		expectError    bool
	}{
		{
			name:           "read kubeconfig fails",
			kubeconfigData: nil,
			mockReadFile: func(filename string) ([]byte, error) {
				return nil, fmt.Errorf("read error")
			},
			expectError: true,
		},
		{
			name:           "empty kubeconfig",
			kubeconfigData: []byte(""),
			mockReadFile: func(filename string) ([]byte, error) {
				return []byte(""), nil
			},
			expectError: true,
		},
		{
			name:           "get user home fails",
			kubeconfigData: []byte(kubeconfigContent),
			mockReadFile: func(filename string) ([]byte, error) {
				return []byte(kubeconfigContent), nil
			},
			mockUserHome: func() (string, error) {
				return "", fmt.Errorf("home error")
			},
			expectError: true,
		},
		{
			name:           "mkdir kube dir fails",
			kubeconfigData: []byte(kubeconfigContent),
			mockReadFile: func(filename string) ([]byte, error) {
				return []byte(kubeconfigContent), nil
			},
			mockUserHome: func() (string, error) {
				return "/tmp/test-home", nil
			},
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return fmt.Errorf("mkdir error")
			},
			expectError: true,
		},
		{
			name:           "write kubeconfig fails",
			kubeconfigData: []byte(kubeconfigContent),
			mockReadFile: func(filename string) ([]byte, error) {
				return []byte(kubeconfigContent), nil
			},
			mockUserHome: func() (string, error) {
				return "/tmp/test-home", nil
			},
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return nil
			},
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return fmt.Errorf("write error")
			},
			expectError: true,
		},
		{
			name:           "success path",
			kubeconfigData: []byte(kubeconfigContent),
			mockReadFile: func(filename string) ([]byte, error) {
				return []byte(kubeconfigContent), nil
			},
			mockUserHome: func() (string, error) {
				return "/tmp/test-home", nil
			},
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return nil
			},
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return nil
			},
			mockRemoveFile: func(name string) error {
				return nil
			},
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patches := gomonkey.NewPatches()
			defer patches.Reset()

			patches.ApplyFunc(os.ReadFile, tt.mockReadFile)
			if tt.mockUserHome != nil {
				patches.ApplyFunc(os.UserHomeDir, tt.mockUserHome)
			}
			if tt.mockMkdirAll != nil {
				patches.ApplyFunc(os.MkdirAll, tt.mockMkdirAll)
			}
			if tt.mockWriteFile != nil {
				patches.ApplyFunc(os.WriteFile, tt.mockWriteFile)
			}
			if tt.mockRemoveFile != nil {
				patches.ApplyFunc(os.Remove, tt.mockRemoveFile)
			}

			path, err := processKubeconfig(testIPAddr, "6443")

			if tt.expectError {
				assert.Error(t, err)
				assert.Empty(t, path)
			} else {
				assert.NoError(t, err)
				assert.NotEmpty(t, path)
			}
		})
	}
}

func TestWaitForKubernetesReadyLogic(t *testing.T) {
	tests := []struct {
		name           string
		kubeconfigPath string
		mockNewClient  func(string) (k8s.KubernetesClient, error)
		expectError    bool
	}{
		{
			name:           "new client fails",
			kubeconfigPath: "/tmp/test-kubeconfig",
			mockNewClient: func(path string) (k8s.KubernetesClient, error) {
				return nil, fmt.Errorf("client error")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalK8s := global.K8s
			defer func() {
				global.K8s = originalK8s
			}()

			global.K8s = nil

			patches := gomonkey.NewPatches()

			defer patches.Reset()

			patches.ApplyFunc(k8s.NewKubernetesClient, tt.mockNewClient)
			patches.ApplyFunc(time.Sleep, func(time.Duration) {
				t.Log("Skipping Sleep for testing")
			})

			err := waitForKubernetesReady(tt.kubeconfigPath)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, global.K8s)
			}
		})
	}
}

func TestWaitForClusterReadyLogic(t *testing.T) {
	tests := []struct {
		name          string
		mockGetClient func() kubernetes.Interface
		expectError   bool
	}{
		{
			name: "get node fails",
			mockGetClient: func() kubernetes.Interface {
				return fake.NewSimpleClientset()
			},
			expectError: false,
		},
		{
			name: "node has taints",
			mockGetClient: func() kubernetes.Interface {
				return fake.NewSimpleClientset(&corev1.Node{
					ObjectMeta: metav1.ObjectMeta{
						Name: utils.LocalKubernetesName,
					},
					Spec: corev1.NodeSpec{
						Taints: []corev1.Taint{
							{
								Key: "test",
							},
						},
					},
				})
			},
			expectError: false,
		},
		{
			name: "node ready with namespace",
			mockGetClient: func() kubernetes.Interface {
				return fake.NewSimpleClientset(
					&corev1.Node{
						ObjectMeta: metav1.ObjectMeta{
							Name: utils.LocalKubernetesName,
						},
					},
					&corev1.Namespace{
						ObjectMeta: metav1.ObjectMeta{
							Name: "kube-system",
						},
					},
				)
			},
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalK8s := global.K8s
			defer func() {
				global.K8s = originalK8s
			}()

			mockClient := &k8s.MockK8sClient{}
			global.K8s = mockClient

			patches := gomonkey.NewPatches()

			defer patches.Reset()

			patches.ApplyFunc((*k8s.MockK8sClient).GetClient, func(m *k8s.MockK8sClient) kubernetes.Interface {
				return tt.mockGetClient()
			})

			patches.ApplyFunc(time.Sleep, func(d time.Duration) {
				t.Log("Skipping Sleep for testing")
			})

			err := waitForClusterReady()
			assert.NoError(t, err)
		})
	}
}

func TestDeployOauthAndUserLogic(t *testing.T) {
	tests := []struct {
		name             string
		onlineImage      string
		otherRepo        string
		hostIP           string
		repo             string
		openFuyaoVersion string
		mockWriteFile    func(string, []byte, os.FileMode) error
		mockExecuteCmd   func(*exec.CommandExecutor, string, ...string) (string, error)
		expectError      bool
	}{
		{
			name:             "write file fails",
			onlineImage:      "",
			otherRepo:        "",
			hostIP:           testIPAddr,
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return fmt.Errorf("write error")
			},
			expectError: true,
		},
		{
			name:             "execute command fails",
			onlineImage:      "",
			otherRepo:        "",
			hostIP:           testIPAddr,
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return nil
			},
			mockExecuteCmd: func(c *exec.CommandExecutor, command string, args ...string) (string, error) {
				return "", fmt.Errorf("command failed")
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc(writeToDir, func(dir string, script string, scriptContent string) error {
				return tt.mockWriteFile(filepath.Join(dir, script), []byte(scriptContent), utils.DefaultFilePermission)
			})

			patches.ApplyFunc((*exec.CommandExecutor).ExecuteCommandWithCombinedOutput, tt.mockExecuteCmd)

			err := deployOauthAndUser(tt.onlineImage, tt.otherRepo, tt.hostIP, tt.repo, tt.openFuyaoVersion)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestGetPodsLogic(t *testing.T) {
	fakeClient := fake.NewSimpleClientset()

	tests := []struct {
		name          string
		namespace     string
		mockGetClient func() kubernetes.Interface
		expectError   bool
	}{
		{
			name:      "get pods from namespace",
			namespace: "openfuyao-system",
			mockGetClient: func() kubernetes.Interface {
				return fakeClient
			},
			expectError: true,
		},
		{
			name:      "get pods with job pods only",
			namespace: "test-namespace",
			mockGetClient: func() kubernetes.Interface {
				return fake.NewSimpleClientset(&corev1.Pod{
					ObjectMeta: metav1.ObjectMeta{
						Name: "test-pod",
						OwnerReferences: []metav1.OwnerReference{
							{
								Kind: "Job",
							},
						},
					},
				})
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalK8s := global.K8s
			defer func() {
				global.K8s = originalK8s
			}()

			global.K8s = &k8s.MockK8sClient{}

			patches := gomonkey.NewPatches()

			defer patches.Reset()

			patches.ApplyFunc((*k8s.MockK8sClient).GetClient, func(m *k8s.MockK8sClient) kubernetes.Interface {
				return tt.mockGetClient()
			})

			podList, err := getPods(tt.mockGetClient(), tt.namespace)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, podList)
			}
		})
	}
}

func TestWaitAllConsolePodRunningLogic(t *testing.T) {
	tests := []struct {
		name             string
		iterationToReady int
		mockGetClient    func() kubernetes.Interface
		expectCalled     bool
	}{
		{
			name:             "pods ready on first check",
			iterationToReady: 0,
			mockGetClient: func() kubernetes.Interface {
				return fake.NewSimpleClientset(
					&corev1.Pod{
						ObjectMeta: metav1.ObjectMeta{
							Name:      "console-pod",
							Namespace: "openfuyao-system",
						},
						Status: corev1.PodStatus{
							Phase: corev1.PodRunning,
						},
					},
					&corev1.Pod{
						ObjectMeta: metav1.ObjectMeta{
							Name:      "ingress-pod",
							Namespace: "ingress-nginx",
						},
						Status: corev1.PodStatus{
							Phase: corev1.PodRunning,
						},
					},
				)
			},
			expectCalled: true,
		},
		{
			name:             "pods ready after retry",
			iterationToReady: 2,
			mockGetClient: func() kubernetes.Interface {
				return fake.NewSimpleClientset(
					&corev1.Pod{
						ObjectMeta: metav1.ObjectMeta{
							Name:      "console-pod",
							Namespace: "openfuyao-system",
						},
						Status: corev1.PodStatus{
							Phase: corev1.PodRunning,
						},
					},
					&corev1.Pod{
						ObjectMeta: metav1.ObjectMeta{
							Name:      "ingress-pod",
							Namespace: "ingress-nginx",
						},
						Status: corev1.PodStatus{
							Phase: corev1.PodRunning,
						},
					},
				)
			},
			expectCalled: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalK8s := global.K8s
			defer func() {
				global.K8s = originalK8s
			}()

			mockClient := &k8s.MockK8sClient{}
			global.K8s = mockClient

			sleepCounter := 0
			patches := gomonkey.NewPatches()
			defer patches.Reset()
			patches.ApplyFunc((*k8s.MockK8sClient).GetClient, func(m *k8s.MockK8sClient) kubernetes.Interface {
				return tt.mockGetClient()
			})

			patches.ApplyFunc(time.Sleep, func(d time.Duration) {
				sleepCounter++
			})

			waitAllConsolePodRunning()

			assert.True(t, tt.expectCalled)
		})
	}
}

func TestDeployCorednsLogic(t *testing.T) {
	tests := []struct {
		name          string
		repo          string
		mockMkdirAll  func(string, os.FileMode) error
		mockWriteFile func(string, []byte, os.FileMode) error
		mockInstall   func(*k8s.MockK8sClient, string, map[string]string, string) error
		expectError   bool
	}{
		{
			name: "mkdir fails",
			repo: "registry.example.com",
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return fmt.Errorf("mkdir error")
			},
			expectError: true,
		},
		{
			name: "write file fails",
			repo: "registry.example.com",
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return nil
			},
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return fmt.Errorf("write error")
			},
			expectError: true,
		},
		{
			name: "install yaml fails",
			repo: "registry.example.com",
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return nil
			},
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return nil
			},
			mockInstall: func(m *k8s.MockK8sClient, file string, params map[string]string, namespace string) error {
				return fmt.Errorf("install error")
			},
			expectError: true,
		},
		{
			name: "success",
			repo: "registry.example.com",
			mockMkdirAll: func(path string, mode os.FileMode) error {
				return nil
			},
			mockWriteFile: func(name string, data []byte, perm os.FileMode) error {
				return nil
			},
			mockInstall: func(m *k8s.MockK8sClient, file string, params map[string]string, namespace string) error {
				return nil
			},
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalK8s := global.K8s
			originalWorkspace := global.Workspace
			defer func() {
				global.K8s = originalK8s
				global.Workspace = originalWorkspace
			}()

			global.Workspace = "/tmp/test-workspace"

			mockClient := &k8s.MockK8sClient{}
			global.K8s = mockClient

			patches := gomonkey.NewPatches()

			defer patches.Reset()

			patches.ApplyFunc(os.MkdirAll, tt.mockMkdirAll)

			patches.ApplyFunc(os.WriteFile, tt.mockWriteFile)

			patches.ApplyFunc((*k8s.MockK8sClient).InstallYaml, tt.mockInstall)

			err := deployCoredns(tt.repo)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestDeployConsoleAllLogic(t *testing.T) {
	config := types.K3sRestartConfig{
		OtherRepo:      "",
		HostIP:         testIPAddr,
		ImageRepo:      "registry.example.com",
		ImageRepoPort:  "5000",
		OtherRepoIp:    "",
		KubernetesPort: "6443",
	}

	tests := []struct {
		name             string
		repo             string
		openFuyaoVersion string
		mockNewK8s       func(string) (k8s.KubernetesClient, error)
		mockDeployCore   func(string) error
		mockDeployCon    func(string, string, string, string) error
		mockWaitCon      func()
		mockGenerate     func() error
		mockK3sRestart   func(types.K3sRestartConfig) error
		mockDeployOauth  func(string, string, string, string) error
		expectError      bool
	}{
		{
			name:             "new k8s client fails",
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockNewK8s: func(path string) (k8s.KubernetesClient, error) {
				return nil, fmt.Errorf("client error")
			},
			expectError: true,
		},
		{
			name:             "deploy coredns fails",
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockNewK8s: func(path string) (k8s.KubernetesClient, error) {
				return &k8s.MockK8sClient{}, nil
			},
			mockDeployCore: func(repo string) error {
				return fmt.Errorf("coredns error")
			},
			expectError: true,
		},
		{
			name:             "deploy console fails",
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockNewK8s: func(path string) (k8s.KubernetesClient, error) {
				return &k8s.MockK8sClient{}, nil
			},
			mockDeployCore: func(repo string) error {
				return nil
			},
			mockDeployCon: func(otherRepo, hostIP, repo, version string) error {
				return fmt.Errorf("console error")
			},
			expectError: true,
		},
		{
			name:             "generate secret fails",
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockNewK8s: func(path string) (k8s.KubernetesClient, error) {
				return &k8s.MockK8sClient{}, nil
			},
			mockDeployCore: func(repo string) error {
				return nil
			},
			mockDeployCon: func(otherRepo, hostIP, repo, version string) error {
				return nil
			},
			mockWaitCon: func() {},
			mockGenerate: func() error {
				return fmt.Errorf("secret error")
			},
			expectError: true,
		},
		{
			name:             "k3s restart fails",
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockNewK8s: func(path string) (k8s.KubernetesClient, error) {
				return &k8s.MockK8sClient{}, nil
			},
			mockDeployCore: func(repo string) error {
				return nil
			},
			mockDeployCon: func(otherRepo, hostIP, repo, version string) error {
				return nil
			},
			mockWaitCon: func() {},
			mockGenerate: func() error {
				return nil
			},
			mockK3sRestart: func(c types.K3sRestartConfig) error {
				return fmt.Errorf("k3s error")
			},
			expectError: true,
		},
		{
			name:             "deploy oauth fails",
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockNewK8s: func(path string) (k8s.KubernetesClient, error) {
				return &k8s.MockK8sClient{}, nil
			},
			mockDeployCore: func(repo string) error {
				return nil
			},
			mockDeployCon: func(otherRepo, hostIP, repo, version string) error {
				return nil
			},
			mockWaitCon: func() {},
			mockGenerate: func() error {
				return nil
			},
			mockK3sRestart: func(c types.K3sRestartConfig) error {
				return nil
			},
			mockDeployOauth: func(otherRepo, hostIP, repo, version string) error {
				return fmt.Errorf("oauth error")
			},
			expectError: true,
		},
		{
			name:             "success",
			repo:             "registry.example.com",
			openFuyaoVersion: "v1.0.0",
			mockNewK8s: func(path string) (k8s.KubernetesClient, error) {
				return &k8s.MockK8sClient{}, nil
			},
			mockDeployCore: func(repo string) error {
				return nil
			},
			mockDeployCon: func(otherRepo, hostIP, repo, version string) error {
				return nil
			},
			mockWaitCon: func() {},
			mockGenerate: func() error {
				return nil
			},
			mockK3sRestart: func(c types.K3sRestartConfig) error {
				return nil
			},
			mockDeployOauth: func(otherRepo, hostIP, repo, version string) error {
				return nil
			},
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalK8s := global.K8s
			defer func() {
				global.K8s = originalK8s
			}()

			global.K8s = nil

			if tt.mockNewK8s != nil {
				patches := gomonkey.NewPatches()
				defer patches.Reset()
				patches.ApplyFunc(k8s.NewKubernetesClient, tt.mockNewK8s)
			}

			if tt.mockDeployCore != nil {
				patches := gomonkey.NewPatches()
				defer patches.Reset()
				patches.ApplyFunc(deployCoredns, tt.mockDeployCore)
			}

			if tt.mockDeployCon != nil {
				patches := gomonkey.NewPatches()
				defer patches.Reset()
				patches.ApplyFunc(deployConsole, tt.mockDeployCon)
			}

			if tt.mockWaitCon != nil {
				patches := gomonkey.NewPatches()
				defer patches.Reset()
				patches.ApplyFunc(waitAllConsolePodRunning, tt.mockWaitCon)
			}

			if tt.mockGenerate != nil {
				patches := gomonkey.NewPatches()
				defer patches.Reset()
				patches.ApplyFunc(generateSecret, tt.mockGenerate)
			}

			if tt.mockK3sRestart != nil {
				patches := gomonkey.NewPatches()
				defer patches.Reset()
				patches.ApplyFunc(k3sRestart, tt.mockK3sRestart)
			}

			if tt.mockDeployOauth != nil {
				patches := gomonkey.NewPatches()
				defer patches.Reset()
				patches.ApplyFunc(deployOauthAndUser, tt.mockDeployOauth)
			}

			err := DeployConsoleAll(config, tt.repo, tt.openFuyaoVersion)

			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}