/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openFuyao 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 utils

import (
	"context"
	"encoding/json"
	"fmt"
	"io/fs"
	"os"
	"os/exec"
	"time"

	"github.com/agiledragon/gomonkey/v2"
	"go.opentelemetry.io/otel/trace"
	cri "k8s.io/cri-api/pkg/apis"
	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
	critesting "k8s.io/cri-api/pkg/apis/testing"
	remote "k8s.io/cri-client/pkg"
	"k8s.io/klog/v2"
)

type FakeFileInfo struct {
	name     string
	contents string
	mode     fs.FileMode
	offset   int
}

func (f *FakeFileInfo) Name() string {
	return f.name
}

func (f *FakeFileInfo) Size() int64 {
	return int64(len(f.contents))
}

func (f *FakeFileInfo) Mode() fs.FileMode {
	return f.mode
}

func (f *FakeFileInfo) ModTime() time.Time {
	return time.Time{}
}

func (f *FakeFileInfo) IsDir() bool {
	return false
}

func (f *FakeFileInfo) Sys() any {
	return nil
}

type MockUtils struct {
	parseJsonStrPatches            *gomonkey.Patches
	StatPatches                    *gomonkey.Patches
	ChmodPatches                   *gomonkey.Patches
	GetPodUIDByDeviceIDPatches     *gomonkey.Patches
	NewRemoteRuntimeServicePatches *gomonkey.Patches
	GetSandboxesPatches            *gomonkey.Patches
	GetSandboxPidPatches           *gomonkey.Patches
	CombinedOutputPatches          *gomonkey.Patches

	ResetFunc []func()
}

func (m *MockUtils) Reset() {
	for _, fn := range m.ResetFunc {
		fn()
	}
}

func (m *MockUtils) mockParseJsonStr(jsonStr string) {
	m.parseJsonStrPatches = gomonkey.ApplyFunc(parseJsonStr, func(string) (map[string]interface{}, error) {
		data := make(map[string]interface{})
		_ = json.Unmarshal([]byte(jsonStr), &data)
		return data, nil
	})
	m.ResetFunc = append(m.ResetFunc, m.parseJsonStrPatches.Reset)
}

func (m *MockUtils) mockStat(name string) {
	switch name {
	case "file not exist":
		m.StatPatches = gomonkey.ApplyFunc(os.Stat, func(string) (os.FileInfo, error) {
			return nil, os.ErrNotExist
		})
	case "file already has permission":
		m.StatPatches = gomonkey.ApplyFunc(os.Stat, func(string) (os.FileInfo, error) {
			return &FakeFileInfo{
				mode: Permission,
			}, nil
		})
	case "file's permission can be set":
		m.StatPatches = gomonkey.ApplyFunc(os.Stat, func(string) (os.FileInfo, error) {
			return &FakeFileInfo{
				mode: 0555,
			}, nil
		})
	default:
	}

	m.ResetFunc = append(m.ResetFunc, m.StatPatches.Reset)
}

func (m *MockUtils) mockChmod() {
	m.ChmodPatches = gomonkey.ApplyFunc(os.Chmod, func(string, os.FileMode) error {
		return nil
	})

	m.ResetFunc = append(m.ResetFunc, m.ChmodPatches.Reset)
}

func (m *MockUtils) MockGetPodUIDByDeviceID() {
	m.GetPodUIDByDeviceIDPatches = gomonkey.ApplyFunc(GetPodUIDByDeviceID, func(string, string) (string, error) {
		return "", nil
	})

	m.ResetFunc = append(m.ResetFunc, m.GetPodUIDByDeviceIDPatches.Reset)
}

func (m *MockUtils) MockNewRemoteRuntimeService() {
	m.NewRemoteRuntimeServicePatches = gomonkey.ApplyFunc(remote.NewRemoteRuntimeService, func(string, time.Duration, trace.TracerProvider, *klog.Logger) (cri.RuntimeService, error) {
		return critesting.NewFakeRuntimeService(), nil
	})

	m.ResetFunc = append(m.ResetFunc, m.NewRemoteRuntimeServicePatches.Reset)
}

func (m *MockUtils) MockGetSandboxes() {
	m.GetSandboxesPatches = gomonkey.ApplyFunc(GetSandboxes, func(context.Context, cri.RuntimeService, string) ([]*runtimeapi.PodSandbox, error) {
		return []*runtimeapi.PodSandbox{
			{Id: "test1"},
		}, nil
	})

	m.ResetFunc = append(m.ResetFunc, m.GetSandboxesPatches.Reset)
}

func (m *MockUtils) MockGetSandboxPid() {
	m.GetSandboxPidPatches = gomonkey.ApplyFunc(GetSandboxPid, func(context.Context, cri.RuntimeService, string) (string, error) {
		return "pid", nil
	})

	m.ResetFunc = append(m.ResetFunc, m.GetSandboxPidPatches.Reset)
}

func (m *MockUtils) MockCombinedOutput(name string) {
	switch name {
	case "setUrmaDevNetworkNS err":
		m.CombinedOutputPatches = gomonkey.ApplyFunc((*exec.Cmd).CombinedOutput, func(c *exec.Cmd) ([]byte, error) {
			return []byte{}, fmt.Errorf("exec failed: %w", os.ErrPermission)
		})
	default:
		m.CombinedOutputPatches = gomonkey.ApplyFunc((*exec.Cmd).CombinedOutput, func(c *exec.Cmd) ([]byte, error) {
			return []byte{}, nil
		})
	}

	m.ResetFunc = append(m.ResetFunc, m.CombinedOutputPatches.Reset)
}