/*
Copyright (c) 2026 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 local

import (
	"fmt"
	"strings"
	"testing"

	ssusdk "atomgit.com/openeuler/ubs-engine.git/src/sdk/go/ssu"
	"github.com/stretchr/testify/assert"

	"gitcode.com/openFuyao/ub-ssu-csi/pkg/backend"
)

func TestNew(t *testing.T) {
	c := New("/run/ubse/ubse.sock")
	assert.NotNil(t, c)

	lc, ok := c.(*localClient)
	assert.True(t, ok)
	assert.Equal(t, "/run/ubse/ubse.sock", lc.SocketPath)
}

func TestAllocVolumeNotSupported(t *testing.T) {
	c := &localClient{}
	result, err := c.AllocVolume("vol-1", 1024, backend.LogicalConfig{})
	assert.Nil(t, result)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "allocVolume not supported in local mode")
}

func TestDeleteVolumeNotSupported(t *testing.T) {
	c := &localClient{}
	err := c.DeleteVolume("vol-1")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "delete volume not supported in local mode")
}

func TestGetConnectionInfoReturnsNil(t *testing.T) {
	c := &localClient{}
	info, err := c.GetConnectionInfo("vol-1")
	assert.Nil(t, info)
	assert.NoError(t, err)
}

func TestGetVolumeStatusEmptyVolumeID(t *testing.T) {
	c := &localClient{}
	status, err := c.GetVolumeStatus("")
	assert.Nil(t, status)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "volumeID must not be empty")
}

func TestGetVolumeInfoEmptyVolumeID(t *testing.T) {
	c := &localClient{}
	info, err := c.GetVolumeInfo("")
	assert.Nil(t, info)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "volumeID must not be empty")
}

func TestSetAccessPermissionValidatesArgs(t *testing.T) {
	c := &localClient{}
	assertAccessPermissionValidation(t, c.SetAccessPermission)
}

func TestRevokeAccessPermissionValidatesArgs(t *testing.T) {
	c := &localClient{}
	assertAccessPermissionValidation(t, c.RevokeAccessPermission)
}

func assertAccessPermissionValidation(t *testing.T, call func(string, string) error) {
	t.Helper()
	tests := []struct {
		name       string
		volumeID   string
		hostNqn    string
		wantSubstr string
	}{
		{name: "empty volumeID", volumeID: "", hostNqn: "hostnqn", wantSubstr: "volumeID must not be empty"},
		{name: "empty hostNqn", volumeID: "vol-1", hostNqn: "", wantSubstr: "hostNqn must not be empty"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := call(tt.volumeID, tt.hostNqn)
			if err == nil || !strings.Contains(err.Error(), tt.wantSubstr) {
				t.Fatalf("expected error containing %q, got %v", tt.wantSubstr, err)
			}
		})
	}
}

func TestGetVolumeStatusHappyPath(t *testing.T) {
	orig := ubsSsuGetNsStats
	ubsSsuGetNsStats = func(name string) ([]ssusdk.UbsSsuNsStats, error) {
		return []ssusdk.UbsSsuNsStats{
			{TotalSize: 1000, UsedSize: 300},
			{TotalSize: 2000, UsedSize: 500},
		}, nil
	}
	defer func() { ubsSsuGetNsStats = orig }()

	c := &localClient{}
	status, err := c.GetVolumeStatus("vol-1")
	assert.NoError(t, err)
	assert.Equal(t, int64(3000), status.TotalBytes)
	assert.Equal(t, int64(800), status.UsedBytes)
	assert.Equal(t, int64(2200), status.AvailableBytes)
}

func TestGetVolumeStatusSDKError(t *testing.T) {
	orig := ubsSsuGetNsStats
	ubsSsuGetNsStats = func(name string) ([]ssusdk.UbsSsuNsStats, error) {
		return nil, fmt.Errorf("sdk error")
	}
	defer func() { ubsSsuGetNsStats = orig }()

	c := &localClient{}
	status, err := c.GetVolumeStatus("vol-1")
	assert.Nil(t, status)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "sdk error")
}

func TestGetVolumeInfoHappyPath(t *testing.T) {
	orig := ubsSsuListAllocInfo
	ubsSsuListAllocInfo = func() ([]ssusdk.UbsSsuAllocResult, error) {
		return []ssusdk.UbsSsuAllocResult{
			{Name: "vol-1", Namespaces: []ssusdk.UbsSsuNamespaceInfo{
				{NsUuid: "uuid-1", NsDevPath: "/dev/ns1", TgtEid: "eid-1", TgtNqn: "nqn-1", NsSize: 1024},
			}},
			{Name: "vol-2"},
		}, nil
	}
	defer func() { ubsSsuListAllocInfo = orig }()

	c := &localClient{}
	info, err := c.GetVolumeInfo("vol-1")
	assert.NoError(t, err)
	assert.Equal(t, "vol-1", info.VolumeID)
	assert.Equal(t, uint64(1024), info.SizeBytes)
	assert.Len(t, info.Namespaces, 1)
}

func TestGetVolumeInfoNotFound(t *testing.T) {
	orig := ubsSsuListAllocInfo
	ubsSsuListAllocInfo = func() ([]ssusdk.UbsSsuAllocResult, error) {
		return []ssusdk.UbsSsuAllocResult{{Name: "vol-other"}}, nil
	}
	defer func() { ubsSsuListAllocInfo = orig }()

	c := &localClient{}
	info, err := c.GetVolumeInfo("vol-1")
	assert.Nil(t, info)
	assert.ErrorIs(t, err, backend.ErrVolumeInfoNotFound)
}

func TestGetVolumeInfoSDKError(t *testing.T) {
	orig := ubsSsuListAllocInfo
	ubsSsuListAllocInfo = func() ([]ssusdk.UbsSsuAllocResult, error) {
		return nil, fmt.Errorf("sdk error")
	}
	defer func() { ubsSsuListAllocInfo = orig }()

	c := &localClient{}
	info, err := c.GetVolumeInfo("vol-1")
	assert.Nil(t, info)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "sdk error")
}

func TestSetAccessPermissionHappyPath(t *testing.T) {
	orig := ubsSsuAddAccessPermission
	ubsSsuAddAccessPermission = func(name string, nqn string) error { return nil }
	defer func() { ubsSsuAddAccessPermission = orig }()

	c := &localClient{}
	err := c.SetAccessPermission("vol-1", "hostnqn")
	assert.NoError(t, err)
}

func TestSetAccessPermissionSDKError(t *testing.T) {
	orig := ubsSsuAddAccessPermission
	ubsSsuAddAccessPermission = func(name string, nqn string) error { return fmt.Errorf("sdk error") }
	defer func() { ubsSsuAddAccessPermission = orig }()

	c := &localClient{}
	err := c.SetAccessPermission("vol-1", "hostnqn")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "sdk error")
}

func TestRevokeAccessPermissionHappyPath(t *testing.T) {
	orig := ubsSsuRemoveAccessPermission
	ubsSsuRemoveAccessPermission = func(name string, nqn string) error { return nil }
	defer func() { ubsSsuRemoveAccessPermission = orig }()

	c := &localClient{}
	err := c.RevokeAccessPermission("vol-1", "hostnqn")
	assert.NoError(t, err)
}

func TestRevokeAccessPermissionSDKError(t *testing.T) {
	orig := ubsSsuRemoveAccessPermission
	ubsSsuRemoveAccessPermission = func(name string, nqn string) error { return fmt.Errorf("sdk error") }
	defer func() { ubsSsuRemoveAccessPermission = orig }()

	c := &localClient{}
	err := c.RevokeAccessPermission("vol-1", "hostnqn")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "sdk error")
}