package builder

import (
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"crypto-miner-server/internal/db"
)

// testWorkspaceRoot walks up from cwd to find the repo root (agent + fusion sources).
func testWorkspaceRoot(t *testing.T) string {
	t.Helper()
	dir, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < 10; i++ {
		if _, err := os.Stat(filepath.Join(dir, "agent", "go.mod")); err == nil {
			if _, err2 := os.Stat(filepath.Join(dir, "fusion", "main.go")); err2 == nil {
				return dir
			}
		}
		parent := filepath.Dir(dir)
		if parent == dir {
			break
		}
		dir = parent
	}
	t.Skip("workspace root (agent/ and fusion/) not found")
	return ""
}

func testHandlerDB(t *testing.T) (*Handler, *db.Database) {
	t.Helper()
	database, err := db.New(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	root := testWorkspaceRoot(t)
	h := &Handler{
		db:          database,
		dataDir:     t.TempDir(),
		agentSrcDir: filepath.Join(root, "agent"),
		projectRoot: root,
		goBinPath:   "go",
	}
	return h, database
}

// setFakeGoFail points goBinPath at a script that always exits non-zero.
func setFakeGoFail(t *testing.T, h *Handler) {
	t.Helper()
	dir := t.TempDir()
	if runtime.GOOS == "windows" {
		p := filepath.Join(dir, "go-fail.bat")
		if err := os.WriteFile(p, []byte("@echo off\r\nexit /b 1\r\n"), 0644); err != nil {
			t.Fatal(err)
		}
		h.goBinPath = p
		return
	}
	p := filepath.Join(dir, "go-fail.sh")
	if err := os.WriteFile(p, []byte("#!/bin/sh\nexit 1\n"), 0755); err != nil {
		t.Fatal(err)
	}
	h.goBinPath = p
}

// setFakeGoSuccess points goBinPath at a script that writes the -o output and exits 0.
func setFakeGoSuccess(t *testing.T, h *Handler) {
	t.Helper()
	dir := t.TempDir()
	if runtime.GOOS == "windows" {
		p := filepath.Join(dir, "go-ok.bat")
		script := "@echo off\r\nsetlocal EnableDelayedExpansion\r\nset \"OUT=\"\r\n" +
			":loop\r\nif \"%~1\"==\"\" goto done\r\nif /I \"%~1\"==\"-o\" (\r\n" +
			"  set \"OUT=%~2\"\r\n  shift\r\n  shift\r\n  goto loop\r\n)\r\n" +
			"shift\r\ngoto loop\r\n:done\r\n" +
			"if defined OUT (\r\n" +
			"  for %%I in (\"!OUT!\") do if not exist \"%%~dpI\" mkdir \"%%~dpI\" 2>nul\r\n" +
			"  echo fake>\"!OUT!\"\r\n" +
			")\r\nexit /b 0\r\n"
		if err := os.WriteFile(p, []byte(script), 0644); err != nil {
			t.Fatal(err)
		}
		h.goBinPath = p
		return
	}
	p := filepath.Join(dir, "go-ok.sh")
	script := "#!/bin/sh\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n" +
		"  if [ \"$1\" = \"-o\" ]; then OUT=\"$2\"; shift; fi\n  shift\n" +
		"done\nif [ -n \"$OUT\" ]; then mkdir -p \"$(dirname \"$OUT\")\"; echo fake > \"$OUT\"; fi\nexit 0\n"
	if err := os.WriteFile(p, []byte(script), 0755); err != nil {
		t.Fatal(err)
	}
	h.goBinPath = p
}

// setFakeGoSleep points goBinPath at a script that blocks long enough to test cancellation.
func setFakeGoSleep(t *testing.T, h *Handler) {
	t.Helper()
	dir := t.TempDir()
	if runtime.GOOS == "windows" {
		p := filepath.Join(dir, "go-sleep.bat")
		if err := os.WriteFile(p, []byte("@echo off\r\nping 127.0.0.1 -n 8 >nul\r\nexit /b 0\r\n"), 0644); err != nil {
			t.Fatal(err)
		}
		h.goBinPath = p
		return
	}
	p := filepath.Join(dir, "go-sleep.sh")
	if err := os.WriteFile(p, []byte("#!/bin/sh\nsleep 8\nexit 0\n"), 0755); err != nil {
		t.Fatal(err)
	}
	h.goBinPath = p
}
