//go:build windows

package hcsv2

import (
	"context"
	"io"
	"syscall"

	"github.com/Microsoft/go-winio"
	diskutil "github.com/Microsoft/go-winio/vhd"
	"github.com/Microsoft/hcsshim/computestorage"
	"github.com/pkg/errors"
	"golang.org/x/sys/windows"
)

// makeOpenFiles wraps each handle in input as an overlapped I/O file, returning
// a parallel slice (typically the stdin/stdout/stderr of a compute-system
// process). On any wrap failure, all opened files are closed and the
// remaining unwrapped handles are closed before returning the error.
//
// Handles equal to 0 (NULL) or INVALID_HANDLE_VALUE are treated as "not
// present" and map to a nil entry in the result. HCS APIs such as
// HcsGetProcessInfo use INVALID_HANDLE_VALUE to indicate std streams the
// caller did not request; passing that value to NewOpenFile would
// incorrectly bind the sentinel as if it were a valid pipe handle.
func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) {
	fs := make([]io.ReadWriteCloser, len(hs))
	for i, h := range hs {
		if h != syscall.Handle(0) && h != syscall.Handle(windows.InvalidHandle) {
			if err == nil {
				fs[i], err = winio.NewOpenFile(windows.Handle(h))
			}
			if err != nil {
				syscall.Close(h)
			}
		}
	}
	if err != nil {
		for _, f := range fs {
			if f != nil {
				f.Close()
			}
		}
		return nil, err
	}
	return fs, nil
}

// CreateNTFSVHD creates a VHD formatted with NTFS of size `sizeGB` at the given `vhdPath`.
func CreateNTFSVHD(ctx context.Context, vhdPath string, sizeGB uint32) (err error) {
	if err := diskutil.CreateVhdx(vhdPath, sizeGB, 1); err != nil {
		return errors.Wrap(err, "failed to create VHD")
	}

	vhd, err := diskutil.OpenVirtualDisk(vhdPath, diskutil.VirtualDiskAccessNone, diskutil.OpenVirtualDiskFlagNone)
	if err != nil {
		return errors.Wrap(err, "failed to open VHD")
	}
	defer func() {
		err2 := windows.CloseHandle(windows.Handle(vhd))
		if err == nil {
			err = errors.Wrap(err2, "failed to close VHD")
		}
	}()

	if err := computestorage.FormatWritableLayerVhd(ctx, windows.Handle(vhd)); err != nil {
		return errors.Wrap(err, "failed to format VHD")
	}

	return nil
}
