// 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 driver

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"testing"

	csi "github.com/container-storage-interface/spec/lib/go/csi"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	testingexec "k8s.io/utils/exec/testing"
	"k8s.io/utils/mount"

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

type fakeMounter struct {
	mountPoints    map[string]bool
	mountPointErr  map[string]error
	unmountErr     error
	mountErr       error
	mountedPaths   []string
	unmountedPaths []string
	mountArgs      []mountCallArg
}

type mountCallArg struct {
	source  string
	target  string
	fstype  string
	options []string
}

func newFakeMounter() *fakeMounter {
	return &fakeMounter{
		mountPoints:   make(map[string]bool),
		mountPointErr: make(map[string]error),
	}
}

func (f *fakeMounter) Mount(source, target, fstype string, options []string) error {
	f.mountedPaths = append(f.mountedPaths, target)
	f.mountArgs = append(f.mountArgs, mountCallArg{
		source:  source,
		target:  target,
		fstype:  fstype,
		options: options,
	})
	if f.mountErr != nil {
		return f.mountErr
	}
	f.mountPoints[target] = true
	return nil
}

func (f *fakeMounter) MountSensitive(source, target, fstype string, options []string, sensitiveOptions []string) error {
	return f.Mount(source, target, fstype, options)
}

func (f *fakeMounter) Unmount(target string) error {
	f.unmountedPaths = append(f.unmountedPaths, target)
	if f.unmountErr != nil {
		return f.unmountErr
	}
	f.mountPoints[target] = false
	return nil
}

func (f *fakeMounter) List() ([]mount.MountPoint, error) {
	return nil, nil
}

func (f *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
	if err, ok := f.mountPointErr[file]; ok {
		return true, err
	}
	return !f.mountPoints[file], nil
}

func (f *fakeMounter) GetMountRefs(pathname string) ([]string, error) {
	return nil, nil
}

type fakeStorageManager struct {
	volStatus         *backend.VolumeStatus
	volStatusErr      error
	allocInfo         *backend.VolumeInfo
	allocInfoErr      error
	setAccessErr      error
	revokeAccessErr   error
	setAccessCalls    []accessCall
	revokeAccessCalls []accessCall
}

type accessCall struct {
	volumeID string
	hostNqn  string
}

func (f *fakeStorageManager) AllocVolume(
	name string, sizeBytes uint64, config backend.LogicalConfig,
) (*backend.VolumeInfo, error) {
	// For testing purposes, return the allocInfo if it matches the requested volume
	if f.allocInfoErr != nil {
		return nil, f.allocInfoErr
	}
	if f.allocInfo != nil && f.allocInfo.VolumeID == name {
		return f.allocInfo, nil
	}
	return nil, backend.ErrVolumeInfoNotFound
}

func (f *fakeStorageManager) DeleteVolume(volumeID string) error { return nil }

func (f *fakeStorageManager) GetVolumeStatus(volumeID string) (*backend.VolumeStatus, error) {
	return f.volStatus, f.volStatusErr
}

func (f *fakeStorageManager) GetVolumeInfo(volumeID string) (*backend.VolumeInfo, error) {
	if f.allocInfoErr != nil {
		return nil, f.allocInfoErr
	}
	if f.allocInfo != nil && f.allocInfo.VolumeID == volumeID {
		return f.allocInfo, nil
	}
	return nil, backend.ErrVolumeInfoNotFound
}

func (f *fakeStorageManager) SetAccessPermission(volumeID string, hostNqn string) error {
	f.setAccessCalls = append(f.setAccessCalls, accessCall{volumeID: volumeID, hostNqn: hostNqn})
	return f.setAccessErr
}

func (f *fakeStorageManager) RevokeAccessPermission(volumeID string, hostNqn string) error {
	f.revokeAccessCalls = append(f.revokeAccessCalls, accessCall{volumeID: volumeID, hostNqn: hostNqn})
	return f.revokeAccessErr
}

func (f *fakeStorageManager) GetConnectionInfo(volumeID string) ([]*backend.ConnectionInfo, error) {
	return nil, nil
}

func newFakeStorageManager() *fakeStorageManager {
	return &fakeStorageManager{}
}

type connectorCall struct {
	name       string
	hostNqn    string
	deviceID   string
	devicePath string
	level      uint8
	chunkSize  uint32
}

type fakeConnector struct {
	connectCalls          []connectorCall
	disconnectCalls       []connectorCall
	linearCalls           []connectorCall
	linearDisconnects     []connectorCall
	stripingCalls         []connectorCall
	stripingDisconnects   []connectorCall
	connectErr            error
	disconnectErr         error
	linearErr             error
	linearDisconnectErr   error
	stripingErr           error
	stripingDisconnectErr error
}

func (f *fakeConnector) Connect(name, hostNqn, deviceID string) error {
	f.connectCalls = append(f.connectCalls, connectorCall{name: name, hostNqn: hostNqn, deviceID: deviceID})
	return f.connectErr
}

func (f *fakeConnector) Disconnect(name, hostNqn string) error {
	f.disconnectCalls = append(f.disconnectCalls, connectorCall{name: name, hostNqn: hostNqn})
	return f.disconnectErr
}

func (f *fakeConnector) ConnectWithStripingAddressing(name string, hostNqn string, devicePath string, level uint8, chunkSize uint32) error {
	f.stripingCalls = append(f.stripingCalls, connectorCall{name: name, hostNqn: hostNqn, devicePath: devicePath, level: level, chunkSize: chunkSize})
	return f.stripingErr
}

func (f *fakeConnector) DisconnectWithStripingAddressing(name, hostNqn, devicePath string) error {
	f.stripingDisconnects = append(f.stripingDisconnects, connectorCall{name: name, hostNqn: hostNqn, devicePath: devicePath})
	return f.stripingDisconnectErr
}

func (f *fakeConnector) ConnectWithLinearAddressing(name, hostNqn, devicePath string) error {
	f.linearCalls = append(f.linearCalls, connectorCall{name: name, hostNqn: hostNqn, devicePath: devicePath})
	return f.linearErr
}

func (f *fakeConnector) DisconnectWithLinearAddressing(name, hostNqn, devicePath string) error {
	f.linearDisconnects = append(f.linearDisconnects, connectorCall{name: name, hostNqn: hostNqn, devicePath: devicePath})
	return f.linearDisconnectErr
}

func newTestNodeServer(storage backend.StorageManager, connector nvme.Connector, mounter mount.Interface, formatter *mount.SafeFormatAndMount) *NodeServer {
	return &NodeServer{
		nodeID:    "test-node",
		hostNqn:   nvme.GenerateHostNQN("test-node"),
		storage:   storage,
		connector: connector,
		mounter:   mounter,
		formatter: formatter,
	}
}

func newDefaultTestNS() *NodeServer {
	return newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, newFakeMounter(), nil)
}

func blockCap() *csi.VolumeCapability {
	return &csi.VolumeCapability{
		AccessType: &csi.VolumeCapability_Block{
			Block: &csi.VolumeCapability_BlockVolume{},
		},
		AccessMode: &csi.VolumeCapability_AccessMode{
			Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
		},
	}
}

func mountCap(fsType string, flags []string) *csi.VolumeCapability {
	return &csi.VolumeCapability{
		AccessType: &csi.VolumeCapability_Mount{
			Mount: &csi.VolumeCapability_MountVolume{
				FsType:     fsType,
				MountFlags: flags,
			},
		},
		AccessMode: &csi.VolumeCapability_AccessMode{
			Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
		},
	}
}

func makeStageReq(volumeID string, stagingPath string, cap *csi.VolumeCapability, volumeCtx map[string]string) *csi.NodeStageVolumeRequest {
	return &csi.NodeStageVolumeRequest{
		VolumeId:          volumeID,
		StagingTargetPath: stagingPath,
		VolumeCapability:  cap,
		VolumeContext:     volumeCtx,
	}
}

func makeBlockPublishReq(targetPath, deviceID string, readonly bool) *csi.NodePublishVolumeRequest {
	return &csi.NodePublishVolumeRequest{
		VolumeId:         "vol-1",
		TargetPath:       targetPath,
		VolumeCapability: blockCap(),
		Readonly:         readonly,
		VolumeContext: map[string]string{
			"deviceID": deviceID,
		},
	}
}

func makeMountPublishReq(targetPath string, allocStrategy string, fstype string, readonly bool) *csi.NodePublishVolumeRequest {
	return &csi.NodePublishVolumeRequest{
		VolumeId:          "vol-1",
		TargetPath:        targetPath,
		StagingTargetPath: allocStrategy,
		VolumeCapability:  mountCap(fstype, nil),
		Readonly:          readonly,
		VolumeContext:     map[string]string{"allocStrategy": allocStrategy},
	}
}

func makeUnpublishReq(volumeId, targetPath string) *csi.NodeUnpublishVolumeRequest {
	return &csi.NodeUnpublishVolumeRequest{
		VolumeId:   volumeId,
		TargetPath: targetPath,
	}
}

func getErrorCode(err error) codes.Code {
	if st, ok := status.FromError(err); ok {
		return st.Code()
	}
	return codes.Unknown
}

func stageNormalBlockVolumeForTest(t *testing.T, server *NodeServer, stagingPath string) {
	t.Helper()
	_, err := server.NodeStageVolume(context.Background(), makeStageReq(
		"vol-1",
		stagingPath,
		blockCap(),
		map[string]string{"deviceID": "dev-1", "allocStrategy": "2"},
	))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
}

func unstageVolumeForTest(t *testing.T, server *NodeServer, stagingPath string) {
	t.Helper()
	_, err := server.NodeUnstageVolume(context.Background(), &csi.NodeUnstageVolumeRequest{
		VolumeId:          "vol-1",
		StagingTargetPath: stagingPath,
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
}

func legacyStageMetadataFile(stagingPath string) string {
	return filepath.Join(filepath.Dir(stagingPath), "."+filepath.Base(stagingPath)+".ssu-stage.json")
}

func writeLegacyStageMetadata(t *testing.T, stagingPath string, devicePath string) {
	t.Helper()
	metadata := fmt.Sprintf(`{"volumeID":"vol-1","devicePath":"%s"}`, devicePath)
	if err := os.WriteFile(legacyStageMetadataFile(stagingPath), []byte(metadata), 0600); err != nil {
		t.Fatalf("write legacy metadata: %v", err)
	}
}

func TestNodeStageVolumeNormalBlock(t *testing.T) {
	stagingPath := filepath.Join(t.TempDir(), "stage")
	storage := newFakeStorageManager()
	connector := &fakeConnector{}
	ns := newTestNodeServer(storage, connector, newFakeMounter(), nil)

	stageNormalBlockVolumeForTest(t, ns, stagingPath)
	if len(storage.setAccessCalls) != 1 {
		t.Fatalf("expected 1 set access call, got %d", len(storage.setAccessCalls))
	}
	if len(connector.connectCalls) != 1 {
		t.Fatalf("expected normal connect call, got %#v", connector.connectCalls)
	}
	if connector.connectCalls[0].deviceID != "dev-1" {
		t.Fatalf("expected deviceID dev-1, got %s", connector.connectCalls[0].deviceID)
	}
	if _, err := os.Stat(legacyStageMetadataFile(stagingPath)); !os.IsNotExist(err) {
		t.Fatalf("expected no stage metadata file, got %v", err)
	}
}

func TestNodeStageVolumeFilesystemFormatsAndMounts(t *testing.T) {
	stagingPath := filepath.Join(t.TempDir(), "stage")
	fm := newFakeMounter()
	formatter := &mount.SafeFormatAndMount{
		Interface: fm,
		Exec:      &testingexec.FakeExec{DisableScripts: true},
	}
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, formatter)

	_, err := ns.NodeStageVolume(context.Background(), makeStageReq(
		"vol-1",
		stagingPath,
		mountCap("", []string{"noatime"}),
		map[string]string{"deviceID": "dev-1", "allocStrategy": "2"},
	))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(fm.mountArgs) != 1 {
		t.Fatalf("expected 1 format mount call, got %d", len(fm.mountArgs))
	}
	call := fm.mountArgs[0]
	if call.source != "/dev/disk/by-id/nvme-dev-1" || call.target != stagingPath || call.fstype != "ext4" {
		t.Fatalf("unexpected format mount call: %#v", call)
	}
	foundNoatime := false
	for _, opt := range call.options {
		if opt == "noatime" {
			foundNoatime = true
		}
	}
	if !foundNoatime {
		t.Fatalf("unexpected mount options: %#v", call.options)
	}
}

func TestNodeStageVolumeLinearAggregated(t *testing.T) {
	stagingPath := filepath.Join(t.TempDir(), "stage")
	connector := &fakeConnector{}
	ns := newTestNodeServer(newFakeStorageManager(), connector, newFakeMounter(), nil)

	_, err := ns.NodeStageVolume(context.Background(), makeStageReq(
		"vol-1",
		stagingPath,
		blockCap(),
		map[string]string{"allocStrategy": "1"},
	))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(connector.linearCalls) != 1 {
		t.Fatalf("expected 1 linear connect call, got %#v", connector.linearCalls)
	}
	if connector.linearCalls[0].devicePath != "/dev/vol-1" {
		t.Fatalf("expected devicePath /dev/vol-1, got %s", connector.linearCalls[0].devicePath)
	}
}

func TestNodeStageVolumeStripingAggregated(t *testing.T) {
	stagingPath := filepath.Join(t.TempDir(), "stage")
	connector := &fakeConnector{}
	ns := newTestNodeServer(newFakeStorageManager(), connector, newFakeMounter(), nil)

	_, err := ns.NodeStageVolume(context.Background(), makeStageReq(
		"vol-1",
		stagingPath,
		blockCap(),
		map[string]string{
			"allocStrategy": "0",
			"raidLevel":     "5",
			"chunkSize":     "64",
			"nsnum":         "3",
		},
	))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(connector.stripingCalls) != 1 {
		t.Fatalf("expected 1 striping connect call, got %#v", connector.stripingCalls)
	}
	if connector.stripingCalls[0].devicePath != "/dev/vol-1" ||
		connector.stripingCalls[0].level != 5 ||
		connector.stripingCalls[0].chunkSize != 64 {
		t.Fatalf("unexpected striping call: %#v", connector.stripingCalls[0])
	}
}

func TestNodeStageVolumeConnectFailureRevokesAccess(t *testing.T) {
	storage := newFakeStorageManager()
	connector := &fakeConnector{connectErr: fmt.Errorf("attach failed")}
	ns := newTestNodeServer(storage, connector, newFakeMounter(), nil)

	_, err := ns.NodeStageVolume(context.Background(), makeStageReq(
		"vol-1",
		filepath.Join(t.TempDir(), "stage"),
		blockCap(),
		map[string]string{"deviceID": "dev-1", "allocStrategy": "2"},
	))
	if getErrorCode(err) != codes.Internal {
		t.Fatalf("expected Internal, got %v", getErrorCode(err))
	}
}

func TestNodeUnstageVolumeDisconnectsNormalFromAllocInfo(t *testing.T) {
	storage := newFakeStorageManager()
	storage.allocInfo = &backend.VolumeInfo{
		VolumeID: "vol-1",
		Strategy: 2, // AllocStrategyNormal
		Namespaces: []backend.Namespace{
			{DeviceUUID: "dev-1", DevicePath: "/dev/nvme0n1"},
		},
	}
	connector := &fakeConnector{}
	ns := newTestNodeServer(storage, connector, newFakeMounter(), nil)

	unstageVolumeForTest(t, ns, filepath.Join(t.TempDir(), "stage"))
	if len(connector.disconnectCalls) != 1 {
		t.Fatalf("expected disconnect, got %#v", connector.disconnectCalls)
	}
	if len(storage.revokeAccessCalls) != 1 {
		t.Fatalf("expected revoke access, got %d", len(storage.revokeAccessCalls))
	}
}

func TestNodeUnstageVolumeAllocInfoMissingReturnsError(t *testing.T) {
	ns := newDefaultTestNS()
	_, err := ns.NodeUnstageVolume(context.Background(), &csi.NodeUnstageVolumeRequest{
		VolumeId:          "vol-1",
		StagingTargetPath: filepath.Join(t.TempDir(), "stage"),
	})
	if getErrorCode(err) != codes.Internal {
		t.Fatalf("expected Internal, got %v", getErrorCode(err))
	}
}

func TestNodeUnstageVolumeDisconnectsLinearFromAllocInfo(t *testing.T) {
	storage := newFakeStorageManager()
	storage.allocInfo = &backend.VolumeInfo{
		VolumeID:   "vol-1",
		Strategy:   1,
		Namespaces: []backend.Namespace{{DevicePath: "/dev/pvc-1"}, {DevicePath: "/dev/pvc-2"}},
	}
	connector := &fakeConnector{}
	fm := newFakeMounter()
	stagingPath := filepath.Join(t.TempDir(), "stage")
	fm.mountPoints[stagingPath] = true
	ns := newTestNodeServer(storage, connector, fm, nil)

	unstageVolumeForTest(t, ns, stagingPath)
	if len(fm.unmountedPaths) != 1 || fm.unmountedPaths[0] != stagingPath {
		t.Fatalf("expected staging path unmounted, got %#v", fm.unmountedPaths)
	}
	if len(connector.linearDisconnects) != 1 {
		t.Fatalf("expected linear disconnect, got %#v", connector.linearDisconnects)
	}
	if connector.linearDisconnects[0].devicePath != "/dev/vol-1" {
		t.Fatalf("expected devicePath /dev/vol-1, got %s", connector.linearDisconnects[0].devicePath)
	}
	if len(storage.revokeAccessCalls) != 1 {
		t.Fatalf("expected revoke access, got %d", len(storage.revokeAccessCalls))
	}
}

func TestNodeUnstageVolumeDisconnectsStripingFromAllocInfo(t *testing.T) {
	storage := newFakeStorageManager()
	storage.allocInfo = &backend.VolumeInfo{
		VolumeID:   "vol-1",
		Strategy:   0,
		Namespaces: []backend.Namespace{{DevicePath: "/dev/pvc-1"}, {DevicePath: "/dev/pvc-2"}},
	}
	connector := &fakeConnector{}
	ns := newTestNodeServer(storage, connector, newFakeMounter(), nil)

	unstageVolumeForTest(t, ns, filepath.Join(t.TempDir(), "stage"))
	if len(connector.stripingDisconnects) != 1 {
		t.Fatalf("expected striping disconnect, got %#v", connector.stripingDisconnects)
	}
	if connector.stripingDisconnects[0].devicePath != "/dev/vol-1" {
		t.Fatalf("expected devicePath /dev/vol-1, got %s", connector.stripingDisconnects[0].devicePath)
	}
	if len(storage.revokeAccessCalls) != 1 {
		t.Fatalf("expected revoke access, got %d", len(storage.revokeAccessCalls))
	}
}

func TestNodePublishVolume_Validation(t *testing.T) {
	ns := newDefaultTestNS()
	tests := []struct {
		name    string
		req     *csi.NodePublishVolumeRequest
		wantMsg string
	}{
		{
			name:    "volume_id_missing",
			req:     &csi.NodePublishVolumeRequest{},
			wantMsg: "volume ID missing",
		},
		{
			name:    "target_path_missing",
			req:     &csi.NodePublishVolumeRequest{VolumeId: "vol-1"},
			wantMsg: "target path missing",
		},
		{
			name:    "volume_capability_missing",
			req:     &csi.NodePublishVolumeRequest{VolumeId: "vol-1", TargetPath: "/tmp/target"},
			wantMsg: "volume capability missing",
		},
		{
			name: "neither_block_nor_mount",
			req: &csi.NodePublishVolumeRequest{
				VolumeId:   "vol-1",
				TargetPath: "/tmp/target",
				VolumeCapability: &csi.VolumeCapability{
					AccessMode: &csi.VolumeCapability_AccessMode{
						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
					},
				},
			},
			wantMsg: "block or mount access type",
		},
		{
			name: "block_device_id_and_aggregate_path_missing",
			req: &csi.NodePublishVolumeRequest{
				VolumeId:         "vol-1",
				TargetPath:       "/tmp/target",
				VolumeCapability: blockCap(),
				VolumeContext:    map[string]string{},
			},
			wantMsg: "deviceID or allocStrategy",
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			_, err := ns.NodePublishVolume(context.Background(), tc.req)
			if getErrorCode(err) != codes.InvalidArgument {
				t.Fatalf("expected InvalidArgument, got %v", getErrorCode(err))
			}
			if !strings.Contains(err.Error(), tc.wantMsg) {
				t.Fatalf("expected error containing %q, got %v", tc.wantMsg, err)
			}
		})
	}
}

func TestNodePublishVolume_BlockModeRoute(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target-block")
	req := makeBlockPublishReq(targetPath, "sda", false)
	resp, err := ns.NodePublishVolume(context.Background(), req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
	if len(fm.mountedPaths) != 1 {
		t.Fatalf("expected 1 mount call, got %d", len(fm.mountedPaths))
	}
	if fm.mountedPaths[0] != targetPath {
		t.Fatalf("expected mount target %s, got %s", targetPath, fm.mountedPaths[0])
	}
}

func TestNodePublishVolume_MountModeRoute(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target-mount")
	req := makeMountPublishReq(targetPath, "/dev/sda", "ext4", false)
	resp, err := ns.NodePublishVolume(context.Background(), req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
	if len(fm.mountedPaths) != 1 {
		t.Fatalf("expected 1 mount call, got %d", len(fm.mountedPaths))
	}
}

func TestNodePublishVolume_Fstype(t *testing.T) {
	tests := []struct {
		name       string
		fstype     string
		wantFstype string
	}{
		{name: "default_ext4", fstype: "", wantFstype: "ext4"},
		{name: "xfs", fstype: "xfs", wantFstype: "xfs"},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			fm := newFakeMounter()
			ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
			targetPath := filepath.Join(t.TempDir(), "target")
			req := makeMountPublishReq(targetPath, "/dev/sda", tc.fstype, false)
			_, err := ns.NodePublishVolume(context.Background(), req)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if len(fm.mountArgs) == 0 {
				t.Fatal("expected at least 1 mount call")
			}
			for _, arg := range fm.mountArgs {
				if arg.fstype != tc.wantFstype {
					t.Errorf("expected fstype %s, got %s", tc.wantFstype, arg.fstype)
				}
			}
		})
	}
}

func TestNodePublishVolume_BlockDeviceResolution(t *testing.T) {
	tests := []struct {
		name       string
		volumeCtx  map[string]string
		wantSource string
	}{
		{
			name:       "deviceID",
			volumeCtx:  map[string]string{"deviceID": "nvme-xxx"},
			wantSource: "/dev/disk/by-id/nvme-nvme-xxx",
		},
		{
			name:       "allocStrategy",
			volumeCtx:  map[string]string{"allocStrategy": "1"},
			wantSource: "/dev/vol-1",
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			fm := newFakeMounter()
			ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
			targetPath := filepath.Join(t.TempDir(), "target")
			req := &csi.NodePublishVolumeRequest{
				VolumeId:         "vol-1",
				TargetPath:       targetPath,
				VolumeCapability: blockCap(),
				VolumeContext:    tc.volumeCtx,
			}
			resp, err := ns.NodePublishVolume(context.Background(), req)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if resp == nil {
				t.Fatal("expected non-nil response")
			}
			if len(fm.mountArgs) != 1 {
				t.Fatalf("expected 1 mount call, got %d", len(fm.mountArgs))
			}
			if fm.mountArgs[0].source != tc.wantSource {
				t.Errorf("expected mount source %s, got %s", tc.wantSource, fm.mountArgs[0].source)
			}
		})
	}
}

func TestNodePublishVolume_MountModeNoDeviceID(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	req := &csi.NodePublishVolumeRequest{
		VolumeId:          "vol-1",
		TargetPath:        targetPath,
		StagingTargetPath: "/dev/sda",
		VolumeCapability:  mountCap("ext4", nil),
		VolumeContext:     map[string]string{},
	}
	resp, err := ns.NodePublishVolume(context.Background(), req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
	if len(fm.mountedPaths) != 1 {
		t.Fatalf("expected 1 mount call, got %d", len(fm.mountedPaths))
	}
}

func TestNodePublishVolumeBlockIgnoresLegacyStageMetadata(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	tmpDir := t.TempDir()
	stagingPath := filepath.Join(tmpDir, "stage")
	targetPath := filepath.Join(tmpDir, "target")
	writeLegacyStageMetadata(t, stagingPath, "/dev/staged-device")

	req := makeBlockPublishReq(targetPath, "context-device", false)
	req.StagingTargetPath = stagingPath
	_, err := ns.NodePublishVolume(context.Background(), req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(fm.mountArgs) != 1 {
		t.Fatalf("expected 1 mount call, got %d", len(fm.mountArgs))
	}
	if fm.mountArgs[0].source != "/dev/disk/by-id/nvme-context-device" {
		t.Fatalf("expected context device, got %s", fm.mountArgs[0].source)
	}
}

func TestNodePublishVolumeBlock_OpenFileCreate(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	if err := os.MkdirAll(targetPath, 0755); err != nil {
		t.Fatalf("setup: %v", err)
	}
	req := makeBlockPublishReq(targetPath, "/dev/sda", false)
	_, err := ns.nodePublishVolumeBlock(req, targetPath, "/dev/sda")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	fi, err := os.Stat(targetPath)
	if err != nil {
		t.Fatalf("expected targetPath to exist after publish: %v", err)
	}
	if fi.IsDir() {
		t.Fatalf("targetPath should be a file, not a directory")
	}
}

func TestNodePublishVolumeBlock_BindMountSuccess(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	req := makeBlockPublishReq(targetPath, "/dev/sda", false)
	resp, err := ns.nodePublishVolumeBlock(req, targetPath, "/dev/sda")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
	if len(fm.mountArgs) != 1 {
		t.Fatalf("expected 1 mount call, got %d", len(fm.mountArgs))
	}
	arg := fm.mountArgs[0]
	if arg.fstype != "" {
		t.Errorf("expected empty fstype for bind mount, got %s", arg.fstype)
	}
	foundBind := false
	for _, opt := range arg.options {
		if opt == "bind" {
			foundBind = true
		}
	}
	if !foundBind {
		t.Error("expected 'bind' in mount options")
	}
}

func TestNodePublishVolumeBlock_BindMountFail(t *testing.T) {
	fm := newFakeMounter()
	fm.mountErr = fmt.Errorf("mount failed")
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	req := makeBlockPublishReq(targetPath, "/dev/sda", false)
	_, err := ns.nodePublishVolumeBlock(req, targetPath, "/dev/sda")
	if getErrorCode(err) != codes.Internal {
		t.Fatalf("expected Internal, got %v", getErrorCode(err))
	}
	if !strings.Contains(err.Error(), "mount block device") {
		t.Fatalf("expected error containing 'mount block device', got %v", err.Error())
	}
}

func TestNodePublishVolumeMount_MkdirAllSuccess(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	req := makeMountPublishReq(targetPath, "/dev/sda", "ext4", false)
	_, err := ns.nodePublishVolumeMount(req, targetPath, "/dev/sda", "ext4", false, nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	fi, err := os.Stat(targetPath)
	if err != nil {
		t.Fatalf("expected targetPath to exist: %v", err)
	}
	if !fi.IsDir() {
		t.Fatalf("targetPath should be a directory, got file")
	}
}

func TestNodePublishVolumeMount_AlreadyMounted(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	fm.mountPoints[targetPath] = true
	req := makeMountPublishReq(targetPath, "/dev/sda", "ext4", false)
	resp, err := ns.nodePublishVolumeMount(req, targetPath, "/dev/sda", "ext4", false, nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
	if len(fm.mountedPaths) != 0 {
		t.Errorf("expected 0 mount calls (already mounted), got %d", len(fm.mountedPaths))
	}
}

func TestNodePublishVolumeMount_NotMounted(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	req := makeMountPublishReq(targetPath, "/dev/sda", "ext4", false)
	resp, err := ns.nodePublishVolumeMount(req, targetPath, "/dev/sda", "ext4", false, nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
	if len(fm.mountedPaths) != 1 {
		t.Errorf("expected 1 mount call, got %d", len(fm.mountedPaths))
	}
}

func TestNodePublishVolumeMount_CheckMountPointError(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	fm.mountPointErr[targetPath] = fmt.Errorf("permission denied")
	_, err := ns.nodePublishVolumeMount(
		&csi.NodePublishVolumeRequest{},
		targetPath, "/dev/sda", "ext4", false, nil,
	)
	if getErrorCode(err) != codes.Internal {
		t.Fatalf("expected Internal, got %v", getErrorCode(err))
	}
	if !strings.Contains(err.Error(), "check mount point") {
		t.Fatalf("expected error containing 'check mount point', got %v", err.Error())
	}
}

func TestNodePublishVolumeMount_CheckMountPointErrNotExist(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	fm.mountPointErr[targetPath] = os.ErrNotExist
	req := makeMountPublishReq(targetPath, "/dev/sda", "ext4", false)
	resp, err := ns.nodePublishVolumeMount(req, targetPath, "/dev/sda", "ext4", false, nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
}

func TestNodePublishVolumeMount_Readonly(t *testing.T) {
	tests := []struct {
		name     string
		readonly bool
		wantOpt  string
		badOpt   string
	}{
		{name: "rw", readonly: false, wantOpt: "rw", badOpt: "ro"},
		{name: "ro", readonly: true, wantOpt: "ro", badOpt: "rw"},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			fm := newFakeMounter()
			ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
			targetPath := filepath.Join(t.TempDir(), "target")
			req := makeMountPublishReq(targetPath, "/dev/sda", "ext4", tc.readonly)
			_, err := ns.nodePublishVolumeMount(req, targetPath, "/dev/sda", "ext4", tc.readonly, nil)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if len(fm.mountArgs) == 0 {
				t.Fatal("expected at least 1 mount call")
			}
			foundWant, foundBad := false, false
			for _, opt := range fm.mountArgs[0].options {
				if opt == tc.wantOpt {
					foundWant = true
				}
				if opt == tc.badOpt {
					foundBad = true
				}
			}
			if !foundWant {
				t.Errorf("expected %q option", tc.wantOpt)
			}
			if foundBad {
				t.Errorf("unexpected %q option", tc.badOpt)
			}
		})
	}
}

func TestNodePublishVolumeMount_FormatAndMountFail(t *testing.T) {
	fm := newFakeMounter()
	fm.mountErr = fmt.Errorf("mount failed")
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	req := makeMountPublishReq(targetPath, "/dev/sda", "ext4", false)
	_, err := ns.nodePublishVolumeMount(req, targetPath, "/dev/sda", "ext4", false, nil)
	if getErrorCode(err) != codes.Internal {
		t.Fatalf("expected Internal, got %v", getErrorCode(err))
	}
	if !strings.Contains(err.Error(), "mount device") {
		t.Fatalf("expected error containing 'mount device', got %v", err.Error())
	}
}

func TestNodeUnpublishVolume_Validation(t *testing.T) {
	ns := newDefaultTestNS()
	tests := []struct {
		name       string
		volumeId   string
		targetPath string
		wantMsg    string
	}{
		{name: "volume_id_missing", volumeId: "", targetPath: "/tmp/target", wantMsg: "volume ID missing"},
		{name: "target_path_missing", volumeId: "vol-1", targetPath: "", wantMsg: "target path missing"},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			req := makeUnpublishReq(tc.volumeId, tc.targetPath)
			_, err := ns.NodeUnpublishVolume(context.Background(), req)
			if getErrorCode(err) != codes.InvalidArgument {
				t.Fatalf("expected InvalidArgument, got %v", getErrorCode(err))
			}
			if !strings.Contains(err.Error(), tc.wantMsg) {
				t.Fatalf("expected error containing %q, got %v", tc.wantMsg, err)
			}
		})
	}
}

func TestNodeUnpublishVolume_NotMountPoint(t *testing.T) {
	fm := newFakeMounter()
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	targetPath := filepath.Join(t.TempDir(), "target")
	req := makeUnpublishReq("vol-1", targetPath)

	resp, err := ns.NodeUnpublishVolume(context.Background(), req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
	if len(fm.unmountedPaths) != 0 {
		t.Fatalf("expected 0 unmount calls, got %d", len(fm.unmountedPaths))
	}
}

func TestNodeUnpublishVolume_IsMountPoint(t *testing.T) {
	fm := newFakeMounter()
	targetPath := filepath.Join(t.TempDir(), "target")
	fm.mountPoints[targetPath] = true
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	req := makeUnpublishReq("vol-1", targetPath)

	resp, err := ns.NodeUnpublishVolume(context.Background(), req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
	if len(fm.unmountedPaths) != 1 {
		t.Fatalf("expected 1 unmount call, got %d", len(fm.unmountedPaths))
	}
}

func TestNodeUnpublishVolume_CheckMountPointError(t *testing.T) {
	fm := newFakeMounter()
	targetPath := filepath.Join(t.TempDir(), "target")
	fm.mountPointErr[targetPath] = fmt.Errorf("permission denied")
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	req := makeUnpublishReq("vol-1", targetPath)

	_, err := ns.NodeUnpublishVolume(context.Background(), req)
	if getErrorCode(err) != codes.Internal {
		t.Fatalf("expected Internal, got %v", getErrorCode(err))
	}
	if !strings.Contains(err.Error(), "check mount point") {
		t.Fatalf("expected error containing 'check mount point', got %v", err)
	}
}

func TestNodeUnpublishVolume_CheckMountPointErrNotExist(t *testing.T) {
	fm := newFakeMounter()
	targetPath := filepath.Join(t.TempDir(), "target")
	fm.mountPointErr[targetPath] = os.ErrNotExist
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	req := makeUnpublishReq("vol-1", targetPath)

	resp, err := ns.NodeUnpublishVolume(context.Background(), req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}
}

func TestNodeUnpublishVolume_UnmountFail(t *testing.T) {
	fm := newFakeMounter()
	targetPath := filepath.Join(t.TempDir(), "target")
	fm.mountPoints[targetPath] = true
	fm.unmountErr = fmt.Errorf("unmount failed")
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	req := makeUnpublishReq("vol-1", targetPath)

	_, err := ns.NodeUnpublishVolume(context.Background(), req)
	if getErrorCode(err) != codes.Internal {
		t.Fatalf("expected Internal, got %v", getErrorCode(err))
	}
	if !strings.Contains(err.Error(), "unmount") {
		t.Fatalf("expected error containing 'unmount', got %v", err)
	}
}

func TestNodeUnpublishVolume_Idempotent(t *testing.T) {
	fm := newFakeMounter()
	targetPath := filepath.Join(t.TempDir(), "target")
	if err := os.MkdirAll(targetPath, 0755); err != nil {
		t.Fatalf("setup: %v", err)
	}
	fm.mountPoints[targetPath] = true
	ns := newTestNodeServer(newFakeStorageManager(), &fakeConnector{}, fm, nil)
	req := makeUnpublishReq("vol-1", targetPath)

	resp, err := ns.NodeUnpublishVolume(context.Background(), req)
	if err != nil {
		t.Fatalf("first call error: %v", err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response")
	}

	resp2, err2 := ns.NodeUnpublishVolume(context.Background(), req)
	if err2 != nil {
		t.Fatalf("second call error: %v", err2)
	}
	if resp2 == nil {
		t.Fatal("expected non-nil response on second call")
	}
}

func TestNodeGetVolumeStats_Validation(t *testing.T) {
	ns := newDefaultTestNS()
	tests := []struct {
		name       string
		volumeId   string
		volumePath string
		wantMsg    string
	}{
		{name: "volume_id_missing", volumeId: "", volumePath: "", wantMsg: "volume ID missing"},
		{name: "volume_path_missing", volumeId: "vol-1", volumePath: "", wantMsg: "volume path missing"},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			req := &csi.NodeGetVolumeStatsRequest{VolumeId: tc.volumeId, VolumePath: tc.volumePath}
			_, err := ns.NodeGetVolumeStats(context.Background(), req)
			if getErrorCode(err) != codes.InvalidArgument {
				t.Fatalf("expected InvalidArgument, got %v", getErrorCode(err))
			}
			if !strings.Contains(err.Error(), tc.wantMsg) {
				t.Fatalf("expected error containing %q, got %v", tc.wantMsg, err)
			}
		})
	}
}

func TestNodeGetVolumeStats_Success(t *testing.T) {
	sm := &fakeStorageManager{
		volStatus: &backend.VolumeStatus{
			TotalBytes:     1000,
			UsedBytes:      500,
			AvailableBytes: 500,
		},
	}
	fm := newFakeMounter()
	fm.mountPoints["/data"] = true
	ns := newTestNodeServer(sm, &fakeConnector{}, fm, nil)
	req := &csi.NodeGetVolumeStatsRequest{VolumeId: "vol-1", VolumePath: "/data"}
	resp, err := ns.NodeGetVolumeStats(context.Background(), req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(resp.Usage) != 1 {
		t.Fatalf("expected 1 usage entry, got %d", len(resp.Usage))
	}
	u := resp.Usage[0]
	if u.Total != 1000 || u.Used != 500 || u.Available != 500 {
		t.Errorf("expected Total=1000 Used=500 Available=500, got Total=%d Used=%d Available=%d", u.Total, u.Used, u.Available)
	}
}

func TestNodeGetVolumeStats_GetVolumeStatusFail(t *testing.T) {
	sm := &fakeStorageManager{volStatusErr: fmt.Errorf("internal error")}
	fm := newFakeMounter()
	fm.mountPoints["/data"] = true
	ns := newTestNodeServer(sm, &fakeConnector{}, fm, nil)
	req := &csi.NodeGetVolumeStatsRequest{VolumeId: "vol-1", VolumePath: "/data"}
	_, err := ns.NodeGetVolumeStats(context.Background(), req)
	if getErrorCode(err) != codes.Internal {
		t.Fatalf("expected Internal, got %v", getErrorCode(err))
	}
	if !strings.Contains(err.Error(), "get volume status") {
		t.Fatalf("expected error containing 'get volume status', got %v", err.Error())
	}
}

func TestNodeGetVolumeStats_NotMountPoint(t *testing.T) {
	ns := newDefaultTestNS()
	req := &csi.NodeGetVolumeStatsRequest{VolumeId: "vol-1", VolumePath: "/not-mounted"}
	_, err := ns.NodeGetVolumeStats(context.Background(), req)
	if getErrorCode(err) != codes.NotFound {
		t.Fatalf("expected NotFound, got %v", getErrorCode(err))
	}
	if !strings.Contains(err.Error(), "not a valid mount point") {
		t.Fatalf("expected error containing 'not a valid mount point', got %v", err.Error())
	}
}

func TestNodeGetCapabilities(t *testing.T) {
	ns := newDefaultTestNS()
	resp, err := ns.NodeGetCapabilities(context.Background(), &csi.NodeGetCapabilitiesRequest{})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(resp.Capabilities) != 2 {
		t.Fatalf("expected 2 capabilities, got %d", len(resp.Capabilities))
	}
	types := make(map[csi.NodeServiceCapability_RPC_Type]bool)
	for _, cap := range resp.Capabilities {
		if rpc := cap.GetRpc(); rpc != nil {
			types[rpc.Type] = true
		}
	}
	if !types[csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME] {
		t.Fatalf("missing STAGE_UNSTAGE_VOLUME capability")
	}
	if !types[csi.NodeServiceCapability_RPC_GET_VOLUME_STATS] {
		t.Fatalf("missing GET_VOLUME_STATS capability")
	}
}

func TestNodeGetInfo(t *testing.T) {
	ns := newDefaultTestNS()
	resp, err := ns.NodeGetInfo(context.Background(), &csi.NodeGetInfoRequest{})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if resp.NodeId != "test-node" {
		t.Fatalf("expected NodeId=test-node, got %s", resp.NodeId)
	}
}