package desktop

import (
	"context"
	"errors"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func TestEnsureNodeModules_SkipsWhenDependenciesPresent(t *testing.T) {
	if _, err := exec.LookPath("npm"); err != nil {
		t.Skip("npm not installed; skipping")
	}
	dir := t.TempDir()
	writeDependencyFixture(t, dir, false)
	if err := ensureNodeModules(context.Background(), dir); err != nil {
		t.Fatalf("ensureNodeModules with matching node_modules: %v", err)
	}
}

func TestNodeModulesReady_DetectsMissingDirectDependency(t *testing.T) {
	if _, err := exec.LookPath("npm"); err != nil {
		t.Skip("npm not installed; skipping")
	}
	dir := t.TempDir()
	writeDependencyFixture(t, dir, true)
	if nodeModulesReady(context.Background(), dir) {
		t.Fatal("nodeModulesReady returned true with rc-tree missing")
	}
}

func writeDependencyFixture(t *testing.T, dir string, requireRCTree bool) {
	t.Helper()
	if err := os.MkdirAll(filepath.Join(dir, "node_modules", "vite"), 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(dir, "node_modules", "vite", "package.json"), []byte(`{"version":"1.2.3"}`), 0o644); err != nil {
		t.Fatal(err)
	}
	dependencies := `"vite":"^1.2.0"`
	if requireRCTree {
		dependencies += `,"rc-tree":"^5.13.1"`
	}
	manifest := `{"dependencies":{` + dependencies + `}}`
	if err := os.WriteFile(filepath.Join(dir, "package.json"), []byte(manifest), 0o644); err != nil {
		t.Fatal(err)
	}
}

func TestEnsureElectronBinary_SkipsWhenPathTxtPresent(t *testing.T) {
	electronDir := t.TempDir()
	electronPkg := filepath.Join(electronDir, "node_modules", "electron")
	if err := os.MkdirAll(electronPkg, 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(electronPkg, "path.txt"), []byte("dist/Electron.app"), 0o644); err != nil {
		t.Fatal(err)
	}
	if err := ensureElectronBinary(context.Background(), electronDir); err != nil {
		t.Fatalf("ensureElectronBinary with path.txt present: %v", err)
	}
}

func TestEnsureElectronBinary_ErrorsWhenInstallScriptMissing(t *testing.T) {
	electronDir := t.TempDir()
	err := ensureElectronBinary(context.Background(), electronDir)
	if err == nil {
		t.Fatal("ensureElectronBinary should error when install.js is missing")
	}
}

func TestEnsureElectronBinary_RetriesWithMirrorOnFailure(t *testing.T) {
	electronDir := t.TempDir()
	electronPkg := filepath.Join(electronDir, "node_modules", "electron")
	if err := os.MkdirAll(electronPkg, 0o755); err != nil {
		t.Fatal(err)
	}
	installScript := filepath.Join(electronPkg, "install.js")
	if err := os.WriteFile(installScript, []byte(""), 0o644); err != nil {
		t.Fatal(err)
	}

	// Stub runElectronInstall to capture env per call. First call fails; second
	// (mirror retry) succeeds — but only if ELECTRON_MIRROR is set.
	original := runElectronInstallFn
	defer func() { runElectronInstallFn = original }()
	var calls [][]string
	runElectronInstallFn = func(ctx context.Context, script, dir string, env []string) error {
		calls = append(calls, env)
		hasMirror := false
		for _, entry := range env {
			if v, ok := strings.CutPrefix(entry, "ELECTRON_MIRROR="); ok && v != "" {
				hasMirror = true
			}
		}
		if !hasMirror {
			return errors.New("network unreachable")
		}
		// Simulate successful install by writing path.txt
		if err := os.WriteFile(filepath.Join(electronPkg, "path.txt"), []byte("dist/Electron.app"), 0o644); err != nil {
			return err
		}
		return nil
	}

	t.Setenv("ELECTRON_MIRROR", "")
	if err := ensureElectronBinary(context.Background(), electronDir); err != nil {
		t.Fatalf("ensureElectronBinary with mirror fallback: %v", err)
	}
	if len(calls) != 2 {
		t.Fatalf("expected 2 install attempts, got %d", len(calls))
	}
}

func TestSelectPackageManager_PrefersNpmWhenAvailable(t *testing.T) {
	if _, err := exec.LookPath("npm"); err != nil {
		t.Skip("npm not installed; skipping")
	}
	name, args := selectPackageManager()
	if name != "npm" {
		t.Fatalf("selectPackageManager = %q, want npm", name)
	}
	if len(args) == 0 || args[0] != "install" {
		t.Fatalf("selectPackageManager args = %v, want [install ...]", args)
	}
}
