// Package helper runs external helpers with optional sandboxing.
package helper

import (
	"context"
	"fmt"
	"io"
	"os"
	"time"

	"git.gensokyo.uk/security/fortify/helper/proc"
)

var WaitDelay = 2 * time.Second

const (
	// FortifyHelper is set to 1 when args fd is enabled and 0 otherwise.
	FortifyHelper = "FORTIFY_HELPER"
	// FortifyStatus is set to 1 when stat fd is enabled and 0 otherwise.
	FortifyStatus = "FORTIFY_STATUS"
)

type Helper interface {
	// Start starts the helper process.
	Start() error
	// Wait blocks until Helper exits.
	Wait() error

	fmt.Stringer
}

func newHelperFiles(
	ctx context.Context,
	wt io.WriterTo,
	stat bool,
	argF func(argsFd, statFd int) []string,
	extraFiles []*os.File,
) (hl *helperFiles, args []string) {
	hl = new(helperFiles)
	hl.ctx = ctx
	hl.useArgsFd = wt != nil
	hl.useStatFd = stat

	hl.extraFiles = new(proc.ExtraFilesPre)
	for _, f := range extraFiles {
		_, v := hl.extraFiles.Append()
		*v = f
	}

	argsFd := -1
	if hl.useArgsFd {
		f := proc.NewWriterTo(wt)
		argsFd = int(proc.InitFile(f, hl.extraFiles))
		hl.files = append(hl.files, f)
	}

	statFd := -1
	if hl.useStatFd {
		f := proc.NewStat(&hl.stat)
		statFd = int(proc.InitFile(f, hl.extraFiles))
		hl.files = append(hl.files, f)
	}

	args = argF(argsFd, statFd)
	return
}

// helperFiles provides a generic wrapper around helper ipc.
type helperFiles struct {
	// whether argsFd is present
	useArgsFd bool
	// whether statFd is present
	useStatFd bool

	// closes statFd
	stat io.Closer
	// deferred extraFiles fulfillment
	files []proc.File
	// passed through to [proc.Fulfill] and [proc.InitFile]
	extraFiles *proc.ExtraFilesPre

	ctx context.Context
}