package daemon

import (
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/jesseduffield/lazygit/pkg/common"
	"github.com/jesseduffield/lazygit/pkg/env"
)

// Sometimes lazygit will be invoked in daemon mode from a parent lazygit process.
// We do this when git lets us supply a program to run within a git command.
// For example, if we want to ensure that a git command doesn't hang due to
// waiting for an editor to save a commit message, we can tell git to invoke lazygit
// as the editor via 'GIT_EDITOR=lazygit', and use the env var
// 'LAZYGIT_DAEMON_KIND=EXIT_IMMEDIATELY' to specify that we want to run lazygit
// as a daemon which simply exits immediately. Any additional arguments we want
// to pass to a daemon can be done via other env vars.

type DaemonKind string

const (
	InteractiveRebase DaemonKind = "INTERACTIVE_REBASE"
	ExitImmediately   DaemonKind = "EXIT_IMMEDIATELY"
)

const (
	DaemonKindEnvKey string = "LAZYGIT_DAEMON_KIND"
	RebaseTODOEnvKey string = "LAZYGIT_REBASE_TODO"
)

type Daemon interface {
	Run() error
}

func Handle(common *common.Common) {
	d := getDaemon(common)
	if d == nil {
		return
	}

	if err := d.Run(); err != nil {
		log.Fatal(err)
	}

	os.Exit(0)
}

func InDaemonMode() bool {
	return getDaemonKind() != ""
}

func getDaemon(common *common.Common) Daemon {
	switch getDaemonKind() {
	case InteractiveRebase:
		return &rebaseDaemon{c: common}
	case ExitImmediately:
		return &exitImmediatelyDaemon{c: common}
	}

	return nil
}

func getDaemonKind() DaemonKind {
	return DaemonKind(os.Getenv(DaemonKindEnvKey))
}

type rebaseDaemon struct {
	c *common.Common
}

func (self *rebaseDaemon) Run() error {
	self.c.Log.Info("Lazygit invoked as interactive rebase demon")
	self.c.Log.Info("args: ", os.Args)

	if strings.HasSuffix(os.Args[1], "git-rebase-todo") {
		if err := os.WriteFile(os.Args[1], []byte(os.Getenv(RebaseTODOEnvKey)), 0o644); err != nil {
			return err
		}
	} else if strings.HasSuffix(os.Args[1], filepath.Join(gitDir(), "COMMIT_EDITMSG")) { // TODO: test
		// if we are rebasing and squashing, we'll see a COMMIT_EDITMSG
		// but in this case we don't need to edit it, so we'll just return
	} else {
		self.c.Log.Info("Lazygit demon did not match on any use cases")
	}

	return nil
}

func gitDir() string {
	dir := env.GetGitDirEnv()
	if dir == "" {
		return ".git"
	}
	return dir
}

type exitImmediatelyDaemon struct {
	c *common.Common
}

func (self *exitImmediatelyDaemon) Run() error {
	return nil
}