package controllers

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/jesseduffield/lazygit/pkg/commands/models"
	"github.com/jesseduffield/lazygit/pkg/gui/context"
	"github.com/jesseduffield/lazygit/pkg/gui/style"
	"github.com/jesseduffield/lazygit/pkg/gui/types"
)

type SubmodulesController struct {
	baseController
	c *ControllerCommon
}

var _ types.IController = &SubmodulesController{}

func NewSubmodulesController(
	controllerCommon *ControllerCommon,
) *SubmodulesController {
	return &SubmodulesController{
		baseController: baseController{},
		c:              controllerCommon,
	}
}

func (self *SubmodulesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
	return []*types.Binding{
		{
			Key:         opts.GetKey(opts.Config.Universal.GoInto),
			Handler:     self.checkSelected(self.enter),
			Description: self.c.Tr.LcEnterSubmodule,
		},
		{
			Key:         opts.GetKey(opts.Config.Universal.Remove),
			Handler:     self.checkSelected(self.remove),
			Description: self.c.Tr.LcRemoveSubmodule,
		},
		{
			Key:         opts.GetKey(opts.Config.Submodules.Update),
			Handler:     self.checkSelected(self.update),
			Description: self.c.Tr.LcSubmoduleUpdate,
		},
		{
			Key:         opts.GetKey(opts.Config.Universal.New),
			Handler:     self.add,
			Description: self.c.Tr.LcAddSubmodule,
		},
		{
			Key:         opts.GetKey(opts.Config.Universal.Edit),
			Handler:     self.checkSelected(self.editURL),
			Description: self.c.Tr.LcEditSubmoduleUrl,
		},
		{
			Key:         opts.GetKey(opts.Config.Submodules.Init),
			Handler:     self.checkSelected(self.init),
			Description: self.c.Tr.LcInitSubmodule,
		},
		{
			Key:         opts.GetKey(opts.Config.Submodules.BulkMenu),
			Handler:     self.openBulkActionsMenu,
			Description: self.c.Tr.LcViewBulkSubmoduleOptions,
			OpensMenu:   true,
		},
		{
			Key:         nil,
			Handler:     self.easterEgg,
			Description: self.c.Tr.EasterEgg,
		},
	}
}

func (self *SubmodulesController) GetOnClick() func() error {
	return self.checkSelected(self.enter)
}

func (self *SubmodulesController) GetOnRenderToMain() func() error {
	return func() error {
		return self.c.Helpers().Diff.WithDiffModeCheck(func() error {
			var task types.UpdateTask
			submodule := self.context().GetSelected()
			if submodule == nil {
				task = types.NewRenderStringTask("No submodules")
			} else {
				prefix := fmt.Sprintf(
					"Name: %s\nPath: %s\nUrl:  %s\n\n",
					style.FgGreen.Sprint(submodule.Name),
					style.FgYellow.Sprint(submodule.Path),
					style.FgCyan.Sprint(submodule.Url),
				)

				file := self.c.Helpers().WorkingTree.FileForSubmodule(submodule)
				if file == nil {
					task = types.NewRenderStringTask(prefix)
				} else {
					cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, self.c.State().GetIgnoreWhitespaceInDiffView())
					task = types.NewRunCommandTaskWithPrefix(cmdObj.GetCmd(), prefix)
				}
			}

			return self.c.RenderToMainViews(types.RefreshMainOpts{
				Pair: self.c.MainViewPairs().Normal,
				Main: &types.ViewUpdateOpts{
					Title: "Submodule",
					Task:  task,
				},
			})
		})
	}
}

func (self *SubmodulesController) enter(submodule *models.SubmoduleConfig) error {
	return self.c.Helpers().Repos.EnterSubmodule(submodule)
}

func (self *SubmodulesController) add() error {
	return self.c.Prompt(types.PromptOpts{
		Title: self.c.Tr.LcNewSubmoduleUrl,
		HandleConfirm: func(submoduleUrl string) error {
			nameSuggestion := filepath.Base(strings.TrimSuffix(submoduleUrl, filepath.Ext(submoduleUrl)))

			return self.c.Prompt(types.PromptOpts{
				Title:          self.c.Tr.LcNewSubmoduleName,
				InitialContent: nameSuggestion,
				HandleConfirm: func(submoduleName string) error {
					return self.c.Prompt(types.PromptOpts{
						Title:          self.c.Tr.LcNewSubmodulePath,
						InitialContent: submoduleName,
						HandleConfirm: func(submodulePath string) error {
							return self.c.WithWaitingStatus(self.c.Tr.LcAddingSubmoduleStatus, func() error {
								self.c.LogAction(self.c.Tr.Actions.AddSubmodule)
								err := self.c.Git().Submodule.Add(submoduleName, submodulePath, submoduleUrl)
								if err != nil {
									_ = self.c.Error(err)
								}

								return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
							})
						},
					})
				},
			})
		},
	})
}

func (self *SubmodulesController) editURL(submodule *models.SubmoduleConfig) error {
	return self.c.Prompt(types.PromptOpts{
		Title:          fmt.Sprintf(self.c.Tr.LcUpdateSubmoduleUrl, submodule.Name),
		InitialContent: submodule.Url,
		HandleConfirm: func(newUrl string) error {
			return self.c.WithWaitingStatus(self.c.Tr.LcUpdatingSubmoduleUrlStatus, func() error {
				self.c.LogAction(self.c.Tr.Actions.UpdateSubmoduleUrl)
				err := self.c.Git().Submodule.UpdateUrl(submodule.Name, submodule.Path, newUrl)
				if err != nil {
					_ = self.c.Error(err)
				}

				return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
			})
		},
	})
}

func (self *SubmodulesController) init(submodule *models.SubmoduleConfig) error {
	return self.c.WithWaitingStatus(self.c.Tr.LcInitializingSubmoduleStatus, func() error {
		self.c.LogAction(self.c.Tr.Actions.InitialiseSubmodule)
		err := self.c.Git().Submodule.Init(submodule.Path)
		if err != nil {
			_ = self.c.Error(err)
		}

		return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
	})
}

func (self *SubmodulesController) openBulkActionsMenu() error {
	return self.c.Menu(types.CreateMenuOptions{
		Title: self.c.Tr.LcBulkSubmoduleOptions,
		Items: []*types.MenuItem{
			{
				LabelColumns: []string{self.c.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(self.c.Git().Submodule.BulkInitCmdObj().ToString())},
				OnPress: func() error {
					return self.c.WithWaitingStatus(self.c.Tr.LcRunningCommand, func() error {
						self.c.LogAction(self.c.Tr.Actions.BulkInitialiseSubmodules)
						err := self.c.Git().Submodule.BulkInitCmdObj().Run()
						if err != nil {
							return self.c.Error(err)
						}

						return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
					})
				},
				Key: 'i',
			},
			{
				LabelColumns: []string{self.c.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(self.c.Git().Submodule.BulkUpdateCmdObj().ToString())},
				OnPress: func() error {
					return self.c.WithWaitingStatus(self.c.Tr.LcRunningCommand, func() error {
						self.c.LogAction(self.c.Tr.Actions.BulkUpdateSubmodules)
						if err := self.c.Git().Submodule.BulkUpdateCmdObj().Run(); err != nil {
							return self.c.Error(err)
						}

						return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
					})
				},
				Key: 'u',
			},
			{
				LabelColumns: []string{self.c.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(self.c.Git().Submodule.BulkDeinitCmdObj().ToString())},
				OnPress: func() error {
					return self.c.WithWaitingStatus(self.c.Tr.LcRunningCommand, func() error {
						self.c.LogAction(self.c.Tr.Actions.BulkDeinitialiseSubmodules)
						if err := self.c.Git().Submodule.BulkDeinitCmdObj().Run(); err != nil {
							return self.c.Error(err)
						}

						return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
					})
				},
				Key: 'd',
			},
		},
	})
}

func (self *SubmodulesController) update(submodule *models.SubmoduleConfig) error {
	return self.c.WithWaitingStatus(self.c.Tr.LcUpdatingSubmoduleStatus, func() error {
		self.c.LogAction(self.c.Tr.Actions.UpdateSubmodule)
		err := self.c.Git().Submodule.Update(submodule.Path)
		if err != nil {
			_ = self.c.Error(err)
		}

		return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
	})
}

func (self *SubmodulesController) remove(submodule *models.SubmoduleConfig) error {
	return self.c.Confirm(types.ConfirmOpts{
		Title:  self.c.Tr.RemoveSubmodule,
		Prompt: fmt.Sprintf(self.c.Tr.RemoveSubmodulePrompt, submodule.Name),
		HandleConfirm: func() error {
			self.c.LogAction(self.c.Tr.Actions.RemoveSubmodule)
			if err := self.c.Git().Submodule.Delete(submodule); err != nil {
				return self.c.Error(err)
			}

			return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES, types.FILES}})
		},
	})
}

func (self *SubmodulesController) easterEgg() error {
	return self.c.PushContext(self.c.Contexts().Snake)
}

func (self *SubmodulesController) checkSelected(callback func(*models.SubmoduleConfig) error) func() error {
	return func() error {
		submodule := self.context().GetSelected()
		if submodule == nil {
			return nil
		}

		return callback(submodule)
	}
}

func (self *SubmodulesController) Context() types.Context {
	return self.context()
}

func (self *SubmodulesController) context() *context.SubmodulesContext {
	return self.c.Contexts().Submodules
}