package keybindings

import (
	"fmt"
	"strings"

	"github.com/charmbracelet/bubbles/key"
)

type SerializableKeyMap struct {
	Up                      []string
	Down                    []string
	PageUp                  []string
	PageDown                []string
	SelectEntry             []string
	SelectEntryAndChangeDir []string
	Left                    []string
	Right                   []string
	TableLeft               []string
	TableRight              []string
	DeleteEntry             []string
	Help                    []string
	Quit                    []string
	JumpStartOfInput        []string
	JumpEndOfInput          []string
	WordLeft                []string
	WordRight               []string
}

func prettifyKeyBinding(kb string) string {
	if kb == "up" {
		return "↑ "
	}
	if kb == "down" {
		return "↓ "
	}
	if kb == "left" {
		return "←"
	}
	if kb == "right" {
		return "→"
	}
	subs := [][]string{
		{"+left", "+← "},
		{"+right", "+→ "},
		{"+down", "+↓ "},
		{"+up", "+↑ "},
		{"pgdown", "pgdn"},
	}
	for _, sub := range subs {
		kb = strings.ReplaceAll(kb, sub[0], sub[1])
	}
	return kb
}

func (s SerializableKeyMap) ToKeyMap() KeyMap {
	if len(s.Up) == 0 {
		panic(fmt.Sprintf("%#v", s))
	}
	return KeyMap{
		Up: key.NewBinding(
			key.WithKeys(s.Up...),
			key.WithHelp(prettifyKeyBinding(s.Up[0]), "scroll up "),
		),
		Down: key.NewBinding(
			key.WithKeys(s.Down...),
			key.WithHelp(prettifyKeyBinding(s.Down[0]), "scroll down "),
		),
		PageUp: key.NewBinding(
			key.WithKeys(s.PageUp...),
			key.WithHelp(prettifyKeyBinding(s.PageUp[0]), "page up "),
		),
		PageDown: key.NewBinding(
			key.WithKeys(s.PageDown...),
			key.WithHelp(prettifyKeyBinding(s.PageDown[0]), "page down "),
		),
		SelectEntry: key.NewBinding(
			key.WithKeys(s.SelectEntry...),
			key.WithHelp(prettifyKeyBinding(s.SelectEntry[0]), "select an entry "),
		),
		SelectEntryAndChangeDir: key.NewBinding(
			key.WithKeys(s.SelectEntryAndChangeDir...),
			key.WithHelp(prettifyKeyBinding(s.SelectEntryAndChangeDir[0]), "select an entry and cd into that directory"),
		),
		Left: key.NewBinding(
			key.WithKeys(s.Left...),
			key.WithHelp(prettifyKeyBinding(s.Left[0]), "move left "),
		),
		Right: key.NewBinding(
			key.WithKeys(s.Right...),
			key.WithHelp(prettifyKeyBinding(s.Right[0]), "move right "),
		),
		TableLeft: key.NewBinding(
			key.WithKeys(s.TableLeft...),
			key.WithHelp(prettifyKeyBinding(s.TableLeft[0]), "scroll the table left "),
		),
		TableRight: key.NewBinding(
			key.WithKeys(s.TableRight...),
			key.WithHelp(prettifyKeyBinding(s.TableRight[0]), "scroll the table right "),
		),
		DeleteEntry: key.NewBinding(
			key.WithKeys(s.DeleteEntry...),
			key.WithHelp(prettifyKeyBinding(s.DeleteEntry[0]), "delete the highlighted entry "),
		),
		Help: key.NewBinding(
			key.WithKeys(s.Help...),
			key.WithHelp(prettifyKeyBinding(s.Help[0]), "help "),
		),
		Quit: key.NewBinding(
			key.WithKeys(s.Quit...),
			key.WithHelp(prettifyKeyBinding(s.Quit[0]), "exit hiSHtory "),
		),
		JumpStartOfInput: key.NewBinding(
			key.WithKeys(s.JumpStartOfInput...),
			key.WithHelp(prettifyKeyBinding(s.JumpStartOfInput[0]), "jump to the start of the input "),
		),
		JumpEndOfInput: key.NewBinding(
			key.WithKeys(s.JumpEndOfInput...),
			key.WithHelp(prettifyKeyBinding(s.JumpEndOfInput[0]), "jump to the end of the input "),
		),
		WordLeft: key.NewBinding(
			key.WithKeys(s.WordLeft...),
			key.WithHelp(prettifyKeyBinding(s.WordLeft[0]), "jump left one word "),
		),
		WordRight: key.NewBinding(
			key.WithKeys(s.WordRight...),
			key.WithHelp(prettifyKeyBinding(s.WordRight[0]), "jump right one word "),
		),
	}
}

func (s SerializableKeyMap) WithDefaults() SerializableKeyMap {
	if len(s.Up) == 0 {
		s.Up = DefaultKeyMap.Up.Keys()
	}
	if len(s.Down) == 0 {
		s.Down = DefaultKeyMap.Down.Keys()
	}
	if len(s.PageUp) == 0 {
		s.PageUp = DefaultKeyMap.PageUp.Keys()
	}
	if len(s.PageDown) == 0 {
		s.PageDown = DefaultKeyMap.PageDown.Keys()
	}
	if len(s.SelectEntry) == 0 {
		s.SelectEntry = DefaultKeyMap.SelectEntry.Keys()
	}
	if len(s.SelectEntryAndChangeDir) == 0 {
		s.SelectEntryAndChangeDir = DefaultKeyMap.SelectEntryAndChangeDir.Keys()
	}
	if len(s.Left) == 0 {
		s.Left = DefaultKeyMap.Left.Keys()
	}
	if len(s.Right) == 0 {
		s.Right = DefaultKeyMap.Right.Keys()
	}
	if len(s.TableLeft) == 0 {
		s.TableLeft = DefaultKeyMap.TableLeft.Keys()
	}
	if len(s.TableRight) == 0 {
		s.TableRight = DefaultKeyMap.TableRight.Keys()
	}
	if len(s.DeleteEntry) == 0 {
		s.DeleteEntry = DefaultKeyMap.DeleteEntry.Keys()
	}
	if len(s.Help) == 0 {
		s.Help = DefaultKeyMap.Help.Keys()
	}
	if len(s.Quit) == 0 {
		s.Quit = DefaultKeyMap.Quit.Keys()
	}
	if len(s.JumpStartOfInput) == 0 {
		s.JumpStartOfInput = DefaultKeyMap.JumpStartOfInput.Keys()
	}
	if len(s.JumpEndOfInput) == 0 {
		s.JumpEndOfInput = DefaultKeyMap.JumpEndOfInput.Keys()
	}
	if len(s.WordLeft) == 0 {
		s.WordLeft = DefaultKeyMap.WordLeft.Keys()
	}
	if len(s.WordRight) == 0 {
		s.WordRight = DefaultKeyMap.WordRight.Keys()
	}
	return s
}

type KeyMap struct {
	Up                      key.Binding
	Down                    key.Binding
	PageUp                  key.Binding
	PageDown                key.Binding
	SelectEntry             key.Binding
	SelectEntryAndChangeDir key.Binding
	Left                    key.Binding
	Right                   key.Binding
	TableLeft               key.Binding
	TableRight              key.Binding
	DeleteEntry             key.Binding
	Help                    key.Binding
	Quit                    key.Binding
	JumpStartOfInput        key.Binding
	JumpEndOfInput          key.Binding
	WordLeft                key.Binding
	WordRight               key.Binding
}

func (k KeyMap) ToSerializable() SerializableKeyMap {
	return SerializableKeyMap{
		Up:                      k.Up.Keys(),
		Down:                    k.Down.Keys(),
		PageUp:                  k.PageUp.Keys(),
		PageDown:                k.PageDown.Keys(),
		SelectEntry:             k.SelectEntry.Keys(),
		SelectEntryAndChangeDir: k.SelectEntryAndChangeDir.Keys(),
		Left:                    k.Left.Keys(),
		Right:                   k.Right.Keys(),
		TableLeft:               k.TableLeft.Keys(),
		TableRight:              k.TableRight.Keys(),
		DeleteEntry:             k.DeleteEntry.Keys(),
		Help:                    k.Help.Keys(),
		Quit:                    k.Quit.Keys(),
		JumpStartOfInput:        k.JumpStartOfInput.Keys(),
		JumpEndOfInput:          k.JumpEndOfInput.Keys(),
		WordLeft:                k.WordLeft.Keys(),
		WordRight:               k.WordRight.Keys(),
	}
}

var fakeTitleKeyBinding key.Binding = key.NewBinding(
	key.WithKeys(""),
	key.WithHelp("hiSHtory: Search your shell history", ""),
)

var fakeEmptyKeyBinding key.Binding = key.NewBinding(
	key.WithKeys(""),
	key.WithHelp("", ""),
)

func (k KeyMap) ShortHelp() []key.Binding {
	return []key.Binding{fakeTitleKeyBinding, k.Help}
}

func (k KeyMap) FullHelp() [][]key.Binding {
	return [][]key.Binding{
		{fakeTitleKeyBinding, k.Up, k.Left, k.SelectEntry, k.SelectEntryAndChangeDir},
		{fakeEmptyKeyBinding, k.Down, k.Right, k.DeleteEntry},
		{fakeEmptyKeyBinding, k.PageUp, k.TableLeft, k.Quit},
		{fakeEmptyKeyBinding, k.PageDown, k.TableRight, k.Help},
	}
}

type Binding struct {
	Keys []string `json:"keys"`
	Help key.Help `json:"help"`
}

var DefaultKeyMap = KeyMap{
	Up: key.NewBinding(
		key.WithKeys("up", "alt+OA", "ctrl+p"),
		key.WithHelp("↑ ", "scroll up "),
	),
	Down: key.NewBinding(
		key.WithKeys("down", "alt+OB", "ctrl+n"),
		key.WithHelp("↓ ", "scroll down "),
	),
	PageUp: key.NewBinding(
		key.WithKeys("pgup"),
		key.WithHelp("pgup", "page up "),
	),
	PageDown: key.NewBinding(
		key.WithKeys("pgdown"),
		key.WithHelp("pgdn", "page down "),
	),
	SelectEntry: key.NewBinding(
		key.WithKeys("enter"),
		key.WithHelp("enter", "select an entry "),
	),
	SelectEntryAndChangeDir: key.NewBinding(
		key.WithKeys("ctrl+x"),
		key.WithHelp("ctrl+x", "select an entry and cd into that directory"),
	),
	Left: key.NewBinding(
		key.WithKeys("left"),
		key.WithHelp("← ", "move left "),
	),
	Right: key.NewBinding(
		key.WithKeys("right"),
		key.WithHelp("→ ", "move right "),
	),
	TableLeft: key.NewBinding(
		key.WithKeys("shift+left"),
		key.WithHelp("shift+← ", "scroll the table left "),
	),
	TableRight: key.NewBinding(
		key.WithKeys("shift+right"),
		key.WithHelp("shift+→ ", "scroll the table right "),
	),
	DeleteEntry: key.NewBinding(
		key.WithKeys("ctrl+k"),
		key.WithHelp("ctrl+k", "delete the highlighted entry "),
	),
	Help: key.NewBinding(
		key.WithKeys("ctrl+h"),
		key.WithHelp("ctrl+h", "help "),
	),
	Quit: key.NewBinding(
		key.WithKeys("esc", "ctrl+c", "ctrl+d"),
		key.WithHelp("esc", "exit hiSHtory "),
	),
	JumpStartOfInput: key.NewBinding(
		key.WithKeys("ctrl+a"),
		key.WithHelp("ctrl+a", "jump to the start of the input "),
	),
	JumpEndOfInput: key.NewBinding(
		key.WithKeys("ctrl+e"),
		key.WithHelp("ctrl+e", "jump to the end of the input "),
	),
	WordLeft: key.NewBinding(
		key.WithKeys("ctrl+left"),
		key.WithHelp("ctrl+left", "jump left one word "),
	),
	WordRight: key.NewBinding(
		key.WithKeys("ctrl+right"),
		key.WithHelp("ctrl+right", "jump right one word "),
	),
}
