package util

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"os/exec"
	"strings"
)

// CommandExists 判断命令是否在 PATH 中可用。
func CommandExists(name string) bool {
	_, err := exec.LookPath(name)
	return err == nil
}

// RunCommand 执行命令并将输出写入提供的 writer。
func RunCommand(ctx context.Context, stdout, stderr io.Writer, name string, args ...string) error {
	cmd := exec.CommandContext(ctx, name, args...)
	if stdout != nil {
		cmd.Stdout = stdout
	}
	if stderr != nil {
		cmd.Stderr = stderr
	}
	return cmd.Run()
}

// RunCommandCombined 执行命令并返回标准输出和错误输出的组合结果。
func RunCommandCombined(ctx context.Context, name string, args ...string) (string, error) {
	cmd := exec.CommandContext(ctx, name, args...)
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf

	err := cmd.Run()
	out := strings.TrimSpace(buf.String())
	if err != nil {
		if out == "" {
			return out, err
		}
		return out, fmt.Errorf("%w: %s", err, out)
	}
	return out, nil
}

// RunCommandCapture 执行命令并分别返回标准输出与错误输出。
func RunCommandCapture(ctx context.Context, name string, args ...string) (string, string, error) {
	cmd := exec.CommandContext(ctx, name, args...)
	var (
		stdoutBuf bytes.Buffer
		stderrBuf bytes.Buffer
	)
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = &stderrBuf

	err := cmd.Run()
	stdout := strings.TrimSpace(stdoutBuf.String())
	stderr := strings.TrimSpace(stderrBuf.String())
	if err != nil {
		if errors.Is(err, context.Canceled) {
			return stdout, stderr, err
		}
		if stderr != "" {
			return stdout, stderr, fmt.Errorf("%w: %s", err, stderr)
		}
		return stdout, stderr, err
	}
	return stdout, stderr, nil
}
