package builder

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// compileGoProjectPlatform runs the Go (or garble) compiler for a specific target.
// ctx cancellation kills the compiler process immediately — used by the forge cancel API.
func (h *Handler) compileGoProjectPlatform(ctx context.Context, dir, outputPath, ldflags string, tags []string, obfuscate bool, platform BuildPlatform) ([]byte, error) {
	env := append(os.Environ(),
		"GOOS="+platform.GOOS,
		"GOARCH="+platform.GOARCH,
		"CGO_ENABLED=0",
	)

	buildArgs := []string{"build", "-trimpath", "-ldflags", ldflags, "-o", outputPath}
	if len(tags) > 0 {
		buildArgs = append(buildArgs, "-tags", strings.Join(tags, ","))
	}
	buildArgs = append(buildArgs, ".")

	// Garble works with any target OS from any host OS; the old windows-only guard was wrong.
	useGarble := obfuscate && h.garblePath != ""
	if obfuscate && !useGarble {
		log.Printf("[Forge] obfuscation requested but garble not in PATH — building plain binary")
	}

	var cmd *exec.Cmd
	if useGarble {
		garbleArgs := append([]string{"-literals", "-tiny"}, buildArgs...)
		cmd = exec.CommandContext(ctx, h.garblePath, garbleArgs...)
	} else {
		cmd = exec.CommandContext(ctx, h.goBinPath, buildArgs...)
	}
	cmd.Dir = dir
	cmd.Env = env

	out, err := cmd.CombinedOutput()
	if err != nil {
		if ctx.Err() != nil {
			return out, fmt.Errorf("build cancelled")
		}
		return out, fmt.Errorf("compile failed (%s): %s", platform.Label(), strings.TrimSpace(string(out)))
	}
	return out, nil
}

// compileWorker compiles one agent binary for a single OS/arch target.
func (h *Handler) compileWorker(ctx context.Context, agentDir, buildDir string, req *BuildRequest, buildID string, platform BuildPlatform, fusionWorker bool) (string, error) {
	name := workerFileName(req.WorkerName, platform, fusionWorker)
	outputPath := filepath.Join(buildDir, platform.Label(), name)
	if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
		return "", err
	}

	configDir := filepath.Join(agentDir, "config")
	if err := os.MkdirAll(configDir, 0755); err != nil {
		return "", err
	}
	if err := os.WriteFile(filepath.Join(configDir, "builtin.go"), []byte(h.generateBuiltinConfig(buildID, req)), 0644); err != nil {
		return "", fmt.Errorf("write builtin config: %w", err)
	}

	ldflags := ldflagsFor(req, platform)
	extra, err := injectPolymorph(agentDir, buildID)
	if err != nil {
		log.Printf("[Forge] polymorph inject: %v", err)
	} else {
		ldflags += extra
	}

	obfuscated := h.shouldObfuscate(req) && h.garblePath != ""
	if _, err := h.compileGoProjectPlatform(ctx, agentDir, outputPath, ldflags, h.buildTagsFor(req), obfuscated, platform); err != nil {
		return "", err
	}
	return outputPath, nil
}
