package builder

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

func TestCompileGoProjectPlatformFakeGoFail(t *testing.T) {
	h, database := testHandlerDB(t)
	defer database.Close()
	setFakeGoFail(t, h)

	dir := t.TempDir()
	out := filepath.Join(dir, "worker.exe")
	if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
		t.Fatal(err)
	}
	_, err := h.compileGoProjectPlatform(context.Background(), dir, out, "-s -w", nil, false,
		BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"})
	if err == nil || !strings.Contains(err.Error(), "windows-amd64") {
		t.Fatalf("expected platform compile error, got %v", err)
	}
}

func TestCompileGoProjectPlatformFakeGoSuccess(t *testing.T) {
	h, database := testHandlerDB(t)
	defer database.Close()
	setFakeGoSuccess(t, h)

	dir := t.TempDir()
	out := filepath.Join(dir, "worker.exe")
	if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
		t.Fatal(err)
	}
	if _, err := h.compileGoProjectPlatform(context.Background(), dir, out, "-s -w", []string{"p2p"}, false,
		BuildPlatform{GOOS: "linux", GOARCH: "amd64", Ext: ""}); err != nil {
		t.Fatalf("fake go success: %v", err)
	}
	if _, err := os.Stat(out); err != nil {
		t.Fatalf("output not created: %v", err)
	}
}

func TestCompileGoProjectPlatformObfuscateWithoutGarble(t *testing.T) {
	h, database := testHandlerDB(t)
	defer database.Close()
	setFakeGoSuccess(t, h)
	h.garblePath = ""

	dir := t.TempDir()
	out := filepath.Join(dir, "worker.exe")
	if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
		t.Fatal(err)
	}
	if _, err := h.compileGoProjectPlatform(context.Background(), dir, out, "-s -w", nil, true,
		BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"}); err != nil {
		t.Fatalf("obfuscate without garble should fall back to plain go: %v", err)
	}
}

func TestCompileGoProjectPlatformCancelled(t *testing.T) {
	h, database := testHandlerDB(t)
	defer database.Close()
	setFakeGoSleep(t, h)

	dir := t.TempDir()
	out := filepath.Join(dir, "worker.exe")
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	errCh := make(chan error, 1)
	go func() {
		_, err := h.compileGoProjectPlatform(ctx, dir, out, "-s -w", nil, false,
			BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"})
		errCh <- err
	}()

	time.Sleep(200 * time.Millisecond)
	cancel()

	select {
	case err := <-errCh:
		if err == nil || !strings.Contains(err.Error(), "cancelled") {
			t.Fatalf("expected cancelled build, got %v", err)
		}
	case <-time.After(10 * time.Second):
		t.Fatal("compile did not stop after context cancel")
	}
}

func TestCompileGoProjectDelegatesToPlatform(t *testing.T) {
	h, database := testHandlerDB(t)
	defer database.Close()
	setFakeGoSuccess(t, h)

	dir := t.TempDir()
	out := filepath.Join(dir, "worker.exe")
	if err := os.MkdirAll(filepath.Dir(out), 0755); err != nil {
		t.Fatal(err)
	}
	if _, err := h.compileGoProject(context.Background(), dir, out, "-s -w", nil, false); err != nil {
		t.Fatalf("compileGoProject: %v", err)
	}
}

func TestCompileWorkerFakeGoSuccess(t *testing.T) {
	h, database := testHandlerDB(t)
	defer database.Close()
	setFakeGoSuccess(t, h)

	buildDir := t.TempDir()
	agentDir := filepath.Join(buildDir, "agent")
	if err := h.copyAgentSource(agentDir); err != nil {
		t.Fatal(err)
	}

	req := &BuildRequest{
		WorkerName: "pc-1",
		ServerURL:  "http://127.0.0.1:8989",
		Wallet:     "48abc",
		MeshP2P:    true,
	}
	out, err := h.compileWorker(context.Background(), agentDir, buildDir, req, "bid-1",
		BuildPlatform{GOOS: "windows", GOARCH: "amd64", Ext: ".exe"}, false)
	if err != nil {
		t.Fatalf("compileWorker: %v", err)
	}
	if _, err := os.Stat(out); err != nil {
		t.Fatalf("compiled worker missing: %v", err)
	}
	builtin, err := os.ReadFile(filepath.Join(agentDir, "config", "builtin.go"))
	if err != nil {
		t.Fatal(err)
	}
	if !strings.Contains(string(builtin), "bid-1") || !strings.Contains(string(builtin), "pc-1") {
		t.Fatalf("builtin config not written: %s", builtin)
	}
}

func TestCompileWorkerFakeGoFail(t *testing.T) {
	h, database := testHandlerDB(t)
	defer database.Close()
	setFakeGoFail(t, h)

	buildDir := t.TempDir()
	agentDir := filepath.Join(buildDir, "agent")
	if err := h.copyAgentSource(agentDir); err != nil {
		t.Fatal(err)
	}

	req := &BuildRequest{WorkerName: "pc", ServerURL: "http://x", Wallet: "48abc"}
	_, err := h.compileWorker(context.Background(), agentDir, buildDir, req, "bid",
		BuildPlatform{GOOS: "linux", GOARCH: "amd64", Ext: ""}, true)
	if err == nil {
		t.Fatal("expected compile failure")
	}
}
