package envoy

import (
	"bytes"
	"context"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"sync"

	"github.com/natefinch/atomic"

	"github.com/pomerium/pomerium/internal/envoy/files"
	"github.com/pomerium/pomerium/internal/log"
)

const (
	embeddedEnvoyPermissions     fs.FileMode = 0o700
	embeddedDirectoryPermissions fs.FileMode = 0o755
)

// OverrideEnvoyPath is an override for using an envoy path instead of the embedded envoy path.
var OverrideEnvoyPath = ""

var (
	embeddedFilesBaseDirectory = filepath.Join(os.TempDir(), "pomerium-embedded-files")
	extractEmbeddedEnvoyOnce   sync.Once
)

func extractEmbeddedEnvoy(ctx context.Context) (outPath string, err error) {
	extractEmbeddedEnvoyOnce.Do(func() {
		// clean up our base directory before starting
		err = os.RemoveAll(embeddedFilesBaseDirectory)
		if err != nil {
			err = fmt.Errorf("error cleaning embedded file directory: (directory=%s): %w", embeddedFilesBaseDirectory, err)
			return
		}

		// create known directory base to clean at startup
		err = os.MkdirAll(embeddedFilesBaseDirectory, embeddedDirectoryPermissions)
		if err != nil {
			err = fmt.Errorf("error creating embedded file directory: (directory=%s): %w", embeddedFilesBaseDirectory, err)
			return
		}

		// build a random temp directory inside our base directory to guarantee permissions
		var tmpDir string
		tmpDir, err = os.MkdirTemp(embeddedFilesBaseDirectory, "envoy-")
		if err != nil {
			err = fmt.Errorf("error creating embedded file tmp directory: (directory=%s): %w", embeddedFilesBaseDirectory, err)
			return
		}

		outPath = filepath.Join(tmpDir, "envoy")

		log.Info(ctx).Str("path", outPath).Msg("extracting envoy binary")
		err = atomic.WriteFile(outPath, bytes.NewReader(files.Binary()))
		if err != nil {
			err = fmt.Errorf("error extracting embedded envoy binary to temporary directory (path=%s): %w", outPath, err)
			return
		}

		err = os.Chmod(outPath, embeddedEnvoyPermissions)
		if err != nil {
			err = fmt.Errorf("error chmoding embedded envoy binary: %w", err)
			return
		}
	})
	return outPath, err
}