// Copyright IBM Corp. 2015, 2025
// SPDX-License-Identifier: BUSL-1.1

package subproc

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

const (
	// ExitSuccess indicates the subprocess completed successfully.
	ExitSuccess = 0

	// ExitFailure indicates the subprocess terminated unsuccessfully.
	ExitFailure = 1

	// ExitTimeout indicates the subprocess timed out before completion.
	ExitTimeout = 2

	// ExitNotRunnable indicates a command cannot be run.
	ExitNotRunnable = 127 // bash-ism
)

// MainFunc is the function that runs for this sub-process.
//
// The return value is a process exit code.
type MainFunc func() int

// Do f if nomad was launched as, "nomad [name]". This process will exit without
// running any other part of Nomad.
func Do(name string, f MainFunc) {
	if len(os.Args) > 1 && os.Args[1] == name {
		os.Exit(f())
	}
}

// Print the given message to standard error.
func Print(format string, args ...any) {
	_, _ = fmt.Fprintf(os.Stderr, format+"\n", args...)
}

// Log the given output to the logger.
//
// r should be a buffer containing output (typically combined stdin + stdout)
// f should be an HCLogger Print method (e.g. log.Debug)
func Log(r io.Reader, f func(msg string, args ...any)) string {
	scanner := bufio.NewScanner(r)
	lines := ""
	for scanner.Scan() {
		line := scanner.Text()
		lines += line + "\n"
		f("sub-process", "OUTPUT", line)
	}
	return strings.TrimSpace(lines)
}

// Context creates a context setup with the given timeout.
func Context(timeout time.Duration) (context.Context, context.CancelFunc) {
	ctx := context.Background()
	return context.WithTimeout(ctx, timeout)
}

// SetExpiration is used to ensure the process terminates, once ctx
// is complete. A short grace period is added to allow any cleanup
// to happen first.
func SetExpiration(ctx context.Context) {
	const graceful = 5 * time.Second
	go func() {
		<-ctx.Done()
		time.Sleep(graceful)
		os.Exit(ExitTimeout)
	}()
}
