package crd

import (
	"bytes"
	"context"
	"fmt"
	"os/exec"

	"github.com/tidbcloud/controlplane9/internal/host"
	"github.com/tidbcloud/controlplane9/internal/verbose"
)

// Applier applies YAML manifests to the selected host cluster.
type Applier struct {
	// Kubeconfig is the path to the selected-host kubeconfig file. If empty, uses default.
	Kubeconfig string
	// Context is the kubectl context for the selected host cluster. If empty, uses current context.
	Context string

	// CommandRunner allows injecting a mock for testing.
	CommandRunner func(cmd *exec.Cmd) ([]byte, error)

	// Selected routes apply through the selected host when the kubeconfig lives remotely.
	Selected *host.Session
}

// Apply pipes YAML data to kubectl apply -f - on the selected host cluster.
func (a *Applier) Apply(ctx context.Context, data []byte) error {
	if len(data) == 0 {
		return nil
	}
	if a.Selected != nil {
		_, err := host.RunKubectl(ctx, a.Selected, data, "apply", "-f", "-")
		return err
	}

	args := a.baseArgs("apply", "-f", "-")
	cmd := exec.CommandContext(ctx, "kubectl", args...)
	cmd.Stdin = bytes.NewReader(data)

	output, err := a.runCommand(cmd)
	if err != nil {
		return fmt.Errorf("kubectl apply: %w\n%s", err, string(output))
	}
	return nil
}

func (a *Applier) baseArgs(args ...string) []string {
	var base []string
	if a.Kubeconfig != "" {
		base = append(base, "--kubeconfig", a.Kubeconfig)
	}
	if a.Context != "" {
		base = append(base, "--context", a.Context)
	}
	return append(base, args...)
}

func (a *Applier) runCommand(cmd *exec.Cmd) ([]byte, error) {
	if a.CommandRunner != nil {
		return a.CommandRunner(cmd)
	}
	return verbose.RunCommand(cmd)
}
