package main

import (
	"fmt"
	"os"
	"time"

	"github.com/jesseduffield/lazygit/pkg/app"
	"github.com/jesseduffield/lazygit/pkg/app/daemon"
	"github.com/jesseduffield/lazygit/pkg/integration/components"
	"github.com/jesseduffield/lazygit/pkg/integration/tests"
	integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
	"github.com/mitchellh/go-ps"
)

// The purpose of this program is to run lazygit with an integration test passed in.
// We could have done the check on TEST_NAME in the root main.go but
// that would mean lazygit would be depending on integration test code which
// would bloat the binary.

// You should not invoke this program directly. Instead you should go through
// go run cmd/integration_test/main.go

func main() {
	dummyBuildInfo := &app.BuildInfo{
		Commit:      "",
		Date:        "",
		Version:     "",
		BuildSource: "integration test",
	}

	integrationTest := getIntegrationTest()

	if os.Getenv(components.WAIT_FOR_DEBUGGER_ENV_VAR) != "" && !daemon.InDaemonMode() {
		println("Waiting for debugger to attach...")
		for !isDebuggerAttached() {
			time.Sleep(time.Millisecond * 100)
		}

		println("Debugger attached, continuing")
	}

	app.Start(dummyBuildInfo, integrationTest)
}

func getIntegrationTest() integrationTypes.IntegrationTest {
	if daemon.InDaemonMode() {
		// if we've invoked lazygit as a daemon from within lazygit,
		// we don't want to pass a test to the rest of the code.
		return nil
	}

	integrationTestName := os.Getenv(components.TEST_NAME_ENV_VAR)
	if integrationTestName == "" {
		panic(fmt.Sprintf(
			"expected %s environment variable to be set, given that we're running an integration test",
			components.TEST_NAME_ENV_VAR,
		))
	}

	lazygitRootDir := os.Getenv(components.LAZYGIT_ROOT_DIR)
	allTests := tests.GetTests(lazygitRootDir)
	for _, candidateTest := range allTests {
		if candidateTest.Name() == integrationTestName {
			return candidateTest
		}
	}

	panic("Could not find integration test with name: " + integrationTestName)
}

// Returns whether we are running under a debugger. It uses a heuristic to find
// out: when using dlv, it starts a debugserver executable (which is part of
// lldb), and the debuggee becomes a child process of that. So if the name of
// our parent process is "debugserver", we run under a debugger. This works even
// if the parent process used to be the shell and you then attach to the running
// executable.
//
// On Mac this works with VS Code, with the Jetbrains Goland IDE, and when using
// dlv attach in a terminal. I have not been able to verify that it works on
// other platforms, it may have to be adapted there.
func isDebuggerAttached() bool {
	process, err := ps.FindProcess(os.Getppid())
	if err != nil {
		return false
	}
	return process.Executable() == "debugserver"
}
