// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package workdir

import (
	"path/filepath"
)

// NormalizePath attempts to transform the given path so that it's relative
// to the working directory, which is our preferred way to present and store
// paths to files and directories within a configuration so that they can
// be portable to operations in other working directories.
//
// It isn't always possible to produce a relative path. For example, on Windows
// the given path might be on a different volume (e.g. drive letter or network
// share) than the working directory.
//
// Note that the result will be relative to the main directory of the receiver,
// which should always be the actual process working directory in normal code,
// but might be some other temporary working directory when in test code.
// If you need to access the file or directory that the result refers to with
// functions that aren't aware of our base directory, you can use something
// like the following, which again should be needed only in test code which
// might need to inspect the filesystem in order to make assertions:
//
//	filepath.Join(d.RootModuleDir(), normalizePathResult)
//
// The above is suitable only for situations where the given path is known
// to be beneath the working directory, which is the typical situation for
// temporary working directories created for automated tests.
func (d *Dir) NormalizePath(given string) string {
	// We need an absolute version of d.mainDir in order for our "Rel"
	// result to be reliable.
	absMain, err := filepath.Abs(d.mainDir)
	if err != nil {
		// Weird, but okay...
		return filepath.Clean(given)
	}

	if !filepath.IsAbs(given) {
		given = filepath.Join(absMain, given)
	}

	ret, err := filepath.Rel(absMain, given)
	if err != nil {
		// It's not always possible to find a relative path. For example,
		// the given path might be on an entirely separate volume
		// (e.g. drive letter or network share) on a Windows system, which
		// always requires an absolute path.
		return filepath.Clean(given)
	}

	return ret
}
