// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

package main

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
)

// makeGoroot constructs a GOROOT-like file structure in outPath,
// which consists of toolchainRoot except for the `go` binary, which
// points to gocross.
//
// It's useful for integrating with tooling that expects to be handed
// a GOROOT, like the Goland IDE or depaware.
func makeGoroot(toolchainRoot, outPath string) error {
	self, err := os.Executable()
	if err != nil {
		return fmt.Errorf("getting gocross's path: %v", err)
	}

	os.RemoveAll(outPath)
	if err := os.MkdirAll(filepath.Join(outPath, "bin"), 0750); err != nil {
		return fmt.Errorf("making %q: %v", outPath, err)
	}
	if err := os.Symlink(self, filepath.Join(outPath, "bin/go")); err != nil {
		return fmt.Errorf("linking gocross into outpath: %v", err)
	}

	if err := linkFarm(toolchainRoot, outPath); err != nil {
		return fmt.Errorf("creating GOROOT link farm: %v", err)
	}
	if err := linkFarm(filepath.Join(toolchainRoot, "bin"), filepath.Join(outPath, "bin")); err != nil {
		return fmt.Errorf("creating GOROOT/bin link farm: %v", err)
	}

	return nil
}

// linkFarm symlinks every entry in srcDir into outDir, unless that
// directory entry already exists.
func linkFarm(srcDir, outDir string) error {
	ents, err := os.ReadDir(srcDir)
	if err != nil {
		return fmt.Errorf("reading %q: %v", srcDir, err)
	}

	for _, ent := range ents {
		dst := filepath.Join(outDir, ent.Name())
		_, err := os.Lstat(dst)
		if errors.Is(err, fs.ErrNotExist) {
			if err := os.Symlink(filepath.Join(srcDir, ent.Name()), dst); err != nil {
				return fmt.Errorf("symlinking %q to %q: %v", ent.Name(), outDir, err)
			}
		} else if err != nil {
			return fmt.Errorf("stat-ing %q: %v", dst, err)
		}
	}

	return nil
}
