package gui

import (
	"fmt"

	"github.com/fatih/color"
	"github.com/go-errors/errors"
	"github.com/jesseduffield/gocui"
	"github.com/jesseduffield/lazydocker/pkg/commands"
	"github.com/jesseduffield/lazydocker/pkg/config"
	"github.com/jesseduffield/lazydocker/pkg/utils"
)

// list panel functions

func (gui *Gui) getVolumeContexts() []string {
	return []string{"config"}
}

func (gui *Gui) getVolumeContextTitles() []string {
	return []string{gui.Tr.ConfigTitle}
}

func (gui *Gui) getSelectedVolume() (*commands.Volume, error) {
	selectedLine := gui.State.Panels.Volumes.SelectedLine
	if selectedLine == -1 {
		return nil, gui.Errors.ErrNoVolumes
	}

	return gui.DockerCommand.Volumes[selectedLine], nil
}

func (gui *Gui) handleVolumesClick(g *gocui.Gui, v *gocui.View) error {
	itemCount := len(gui.DockerCommand.Volumes)
	handleSelect := gui.handleVolumeSelect
	selectedLine := &gui.State.Panels.Volumes.SelectedLine

	return gui.handleClick(v, itemCount, selectedLine, handleSelect)
}

func (gui *Gui) handleVolumeSelect(g *gocui.Gui, v *gocui.View) error {
	volume, err := gui.getSelectedVolume()
	if err != nil {
		if err != gui.Errors.ErrNoVolumes {
			return err
		}
		return gui.renderString(g, "main", gui.Tr.NoVolumes)
	}

	gui.focusPoint(0, gui.State.Panels.Volumes.SelectedLine, len(gui.DockerCommand.Volumes), v)

	key := "volumes-" + volume.Name + "-" + gui.getVolumeContexts()[gui.State.Panels.Volumes.ContextIndex]
	if !gui.shouldRefresh(key) {
		return nil
	}

	mainView := gui.getMainView()
	mainView.Tabs = gui.getVolumeContextTitles()
	mainView.TabIndex = gui.State.Panels.Volumes.ContextIndex

	switch gui.getVolumeContexts()[gui.State.Panels.Volumes.ContextIndex] {
	case "config":
		if err := gui.renderVolumeConfig(mainView, volume); err != nil {
			return err
		}
	default:
		return errors.New("Unknown context for Volumes panel")
	}

	return nil
}

func (gui *Gui) renderVolumeConfig(mainView *gocui.View, volume *commands.Volume) error {
	return gui.T.NewTask(func(stop chan struct{}) {
		mainView.Autoscroll = false
		mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel

		padding := 15
		output := ""
		output += utils.WithPadding("Name: ", padding) + volume.Name + "\n"
		output += utils.WithPadding("Driver: ", padding) + volume.Volume.Driver + "\n"
		output += utils.WithPadding("Scope: ", padding) + volume.Volume.Scope + "\n"
		output += utils.WithPadding("Mountpoint: ", padding) + volume.Volume.Mountpoint + "\n"
		output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, volume.Volume.Labels) + "\n"
		output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, volume.Volume.Options) + "\n"

		output += utils.WithPadding("Status: ", padding)
		if volume.Volume.Status != nil {
			output += "\n"
			for k, v := range volume.Volume.Status {
				output += utils.FormatMapItem(padding, k, v)
			}
		} else {
			output += "n/a"
		}

		if volume.Volume.UsageData != nil {
			output += utils.WithPadding("RefCount: ", padding) + fmt.Sprintf("%d", volume.Volume.UsageData.RefCount) + "\n"
			output += utils.WithPadding("Size: ", padding) + utils.FormatBinaryBytes(int(volume.Volume.UsageData.Size)) + "\n"
		}

		_ = gui.renderString(gui.g, "main", output)
	})
}

func (gui *Gui) refreshVolumes() error {
	volumesView := gui.getVolumesView()
	if volumesView == nil {
		// if the volumesView hasn't been instantiated yet we just return
		return nil
	}
	if err := gui.DockerCommand.RefreshVolumes(); err != nil {
		return err
	}

	if len(gui.DockerCommand.Volumes) > 0 && gui.State.Panels.Volumes.SelectedLine == -1 {
		gui.State.Panels.Volumes.SelectedLine = 0
	}
	if len(gui.DockerCommand.Volumes)-1 < gui.State.Panels.Volumes.SelectedLine {
		gui.State.Panels.Volumes.SelectedLine = len(gui.DockerCommand.Volumes) - 1
	}

	gui.g.Update(func(g *gocui.Gui) error {
		volumesView.Clear()
		isFocused := gui.g.CurrentView().Name() == "volumes"
		list, err := utils.RenderList(gui.DockerCommand.Volumes, utils.IsFocused(isFocused))
		if err != nil {
			return err
		}
		fmt.Fprint(volumesView, list)

		if volumesView == g.CurrentView() {
			return gui.handleVolumeSelect(g, volumesView)
		}
		return nil
	})

	return nil
}

func (gui *Gui) handleVolumesNextLine(g *gocui.Gui, v *gocui.View) error {
	if gui.popupPanelFocused() || gui.g.CurrentView() != v {
		return nil
	}

	panelState := gui.State.Panels.Volumes
	gui.changeSelectedLine(&panelState.SelectedLine, len(gui.DockerCommand.Volumes), false)

	return gui.handleVolumeSelect(gui.g, v)
}

func (gui *Gui) handleVolumesPrevLine(g *gocui.Gui, v *gocui.View) error {
	if gui.popupPanelFocused() || gui.g.CurrentView() != v {
		return nil
	}

	panelState := gui.State.Panels.Volumes
	gui.changeSelectedLine(&panelState.SelectedLine, len(gui.DockerCommand.Volumes), true)

	return gui.handleVolumeSelect(gui.g, v)
}

func (gui *Gui) handleVolumesNextContext(g *gocui.Gui, v *gocui.View) error {
	contexts := gui.getVolumeContexts()
	if gui.State.Panels.Volumes.ContextIndex >= len(contexts)-1 {
		gui.State.Panels.Volumes.ContextIndex = 0
	} else {
		gui.State.Panels.Volumes.ContextIndex++
	}

	_ = gui.handleVolumeSelect(gui.g, v)

	return nil
}

func (gui *Gui) handleVolumesPrevContext(g *gocui.Gui, v *gocui.View) error {
	contexts := gui.getVolumeContexts()
	if gui.State.Panels.Volumes.ContextIndex <= 0 {
		gui.State.Panels.Volumes.ContextIndex = len(contexts) - 1
	} else {
		gui.State.Panels.Volumes.ContextIndex--
	}

	_ = gui.handleVolumeSelect(gui.g, v)

	return nil
}

type removeVolumeOption struct {
	description string
	command     string
	force       bool
	runCommand  bool
}

// GetDisplayStrings is a function.
func (r *removeVolumeOption) GetDisplayStrings(isFocused bool) []string {
	return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
}

func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
	volume, err := gui.getSelectedVolume()
	if err != nil {
		return nil
	}

	options := []*removeVolumeOption{
		{
			description: gui.Tr.Remove,
			command:     utils.WithShortSha("docker volume rm " + volume.Name),
			force:       false,
			runCommand:  true,
		},
		{
			description: gui.Tr.ForceRemove,
			command:     utils.WithShortSha("docker volume rm --force " + volume.Name),
			force:       true,
			runCommand:  true,
		},
		{
			description: gui.Tr.Cancel,
			runCommand:  false,
		},
	}

	handleMenuPress := func(index int) error {
		if !options[index].runCommand {
			return nil
		}
		return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
			if cerr := volume.Remove(options[index].force); cerr != nil {
				return gui.createErrorPanel(gui.g, cerr.Error())
			}
			return nil
		})
	}

	return gui.createMenu("", options, len(options), handleMenuPress)
}

func (gui *Gui) handlePruneVolumes() error {
	return gui.createConfirmationPanel(gui.g, gui.getVolumesView(), gui.Tr.Confirm, gui.Tr.ConfirmPruneVolumes, func(g *gocui.Gui, v *gocui.View) error {
		return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
			err := gui.DockerCommand.PruneVolumes()
			if err != nil {
				return gui.createErrorPanel(gui.g, err.Error())
			}
			return nil
		})
	}, nil)
}

func (gui *Gui) handleVolumesCustomCommand(g *gocui.Gui, v *gocui.View) error {
	volume, err := gui.getSelectedVolume()
	if err != nil {
		return nil
	}

	commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
		Volume: volume,
	})

	customCommands := gui.Config.UserConfig.CustomCommands.Volumes

	return gui.createCustomCommandMenu(customCommands, commandObject)
}

func (gui *Gui) handleVolumesBulkCommand(g *gocui.Gui, v *gocui.View) error {
	baseBulkCommands := []config.CustomCommand{
		{
			Name:             gui.Tr.PruneVolumes,
			InternalFunction: gui.handlePruneVolumes,
		},
	}

	bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Volumes...)
	commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})

	return gui.createBulkCommandMenu(bulkCommands, commandObject)
}
