package ui

import (
	"log"

	"github.com/charmbracelet/bubbles/key"
	tea "github.com/charmbracelet/bubbletea"
	bus "github.com/lmika/events"
	"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
	"lmika.dev/cmd/dynamo-browse/internal/common/ui/events"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/keybindings"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/colselector"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/dialogprompt"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/dynamoitemedit"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/dynamoitemview"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/dynamotableview"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/layout"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/relselector"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/statusandprompt"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/styles"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/tableselect"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/utils"
)

const (
	ViewModeTablePrimary   = 0
	ViewModeTableItemEqual = 1
	ViewModeItemPrimary    = 2
	ViewModeItemOnly       = 3
	ViewModeTableOnly      = 4

	ViewModeCount = 5
)

type Model struct {
	tableReadController  *controllers.TableReadController
	tableWriteController *controllers.TableWriteController
	settingsController   *controllers.SettingsController
	exportController     *controllers.ExportController
	commandController    *commandctrl.CommandController
	jobController        *controllers.JobsController
	colSelector          *colselector.Model
	relSelector          *relselector.Model
	itemEdit             *dynamoitemedit.Model
	statusAndPrompt      *statusandprompt.StatusAndPrompt
	tableSelect          *tableselect.Model
	eventBus             *bus.Bus

	mainViewIndex int

	root                 tea.Model
	tableView            *dynamotableview.Model
	itemView             *dynamoitemview.Model
	mainView             tea.Model
	keyMap               *keybindings.ViewKeyBindings
	keyBindingController *controllers.KeyBindingController
}

func NewModel(
	rc *controllers.TableReadController,
	wc *controllers.TableWriteController,
	columnsController *controllers.ColumnsController,
	exportController *controllers.ExportController,
	settingsController *controllers.SettingsController,
	jobController *controllers.JobsController,
	itemRendererService *itemrenderer.Service,
	cc *commandctrl.CommandController,
	eventBus *bus.Bus,
	keyBindingController *controllers.KeyBindingController,
	pasteboardProvider services.PasteboardProvider,
	defaultKeyMap *keybindings.KeyBindings,
) Model {
	uiStyles := styles.DefaultStyles

	dtv := dynamotableview.New(defaultKeyMap.TableView, columnsController, settingsController, eventBus, uiStyles)
	div := dynamoitemview.New(itemRendererService, uiStyles)
	mainView := layout.NewVBox(layout.LastChildFixedAt(14), dtv, div)

	colSelector := colselector.New(mainView, defaultKeyMap, columnsController)
	relSelector := relselector.New(colSelector)
	itemEdit := dynamoitemedit.NewModel(relSelector)
	statusAndPrompt := statusandprompt.New(itemEdit, pasteboardProvider, "", uiStyles.StatusAndPrompt)
	dialogPrompt := dialogprompt.New(statusAndPrompt)
	tableSelect := tableselect.New(dialogPrompt, uiStyles)

	root := layout.FullScreen(tableSelect)

	return Model{
		tableReadController:  rc,
		tableWriteController: wc,
		commandController:    cc,
		exportController:     exportController,
		jobController:        jobController,
		itemEdit:             itemEdit,
		colSelector:          colSelector,
		relSelector:          relSelector,
		statusAndPrompt:      statusAndPrompt,
		tableSelect:          tableSelect,
		root:                 root,
		tableView:            dtv,
		itemView:             div,
		mainView:             mainView,
		keyMap:               defaultKeyMap.View,
		keyBindingController: keyBindingController,
	}
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case controllers.SetTableItemView:
		cmd := m.setMainViewIndex(msg.ViewIndex)
		return m, cmd
	case events.ResultSetUpdated:
		return m, tea.Batch(
			m.tableView.Refresh(),
			events.SetStatus(msg.StatusMessage),
		)
	case tea.KeyMsg:
		// TODO: use modes here
		if !m.statusAndPrompt.InPrompt() && !m.tableSelect.Visible() && !m.colSelector.ColSelectorVisible() && !m.relSelector.SelectorVisible() {
			switch {
			case key.Matches(msg, m.keyMap.Mark):
				if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
					return m, events.SetTeaMessage(m.tableWriteController.ToggleMark(idx))
				}
			case key.Matches(msg, m.keyMap.CopyItemToClipboard):
				if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
					return m, events.SetTeaMessage(m.tableReadController.CopyItemToClipboard(idx))
				}
			case key.Matches(msg, m.keyMap.CopyTableToClipboard):
				return m, events.SetTeaMessage(m.exportController.ExportCSVToClipboard())
			case key.Matches(msg, m.keyMap.Rescan):
				return m, m.tableReadController.Rescan
			case key.Matches(msg, m.keyMap.PromptForQuery):
				return m, m.tableReadController.PromptForQuery
			case key.Matches(msg, m.keyMap.PromptForFilter):
				return m, m.tableReadController.PromptForFilter
			case key.Matches(msg, m.keyMap.FetchNextPage):
				return m, m.tableReadController.NextPage
			case key.Matches(msg, m.keyMap.ViewBack):
				return m, m.tableReadController.ViewBack
			case key.Matches(msg, m.keyMap.ViewForward):
				return m, m.tableReadController.ViewForward
			case key.Matches(msg, m.keyMap.CycleLayoutForward):
				return m, events.SetTeaMessage(controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, 1, ViewModeCount)})
			case key.Matches(msg, m.keyMap.CycleLayoutBackwards):
				return m, events.SetTeaMessage(controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, -1, ViewModeCount)})
			//case "e":
			//	m.itemEdit.Visible()
			//	return m, nil
			case key.Matches(msg, m.keyMap.ShowColumnOverlay):
				return m, events.SetTeaMessage(controllers.ShowColumnOverlay{})
			//case key.Matches(msg, m.keyMap.ShowRelItemsOverlay):
			//	if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
			//		return m, events.SetTeaMessage(m.scriptController.LookupRelatedItems(idx))
			//	}
			case key.Matches(msg, m.keyMap.PromptForCommand):
				return m, m.commandController.Prompt
			case key.Matches(msg, m.keyMap.PromptForTable):
				return m, events.SetTeaMessage(m.tableReadController.ListTables(false))
			case key.Matches(msg, m.keyMap.CancelRunningJob):
				return m, events.SetTeaMessage(m.jobController.CancelRunningJob(m.promptToQuit))
			case key.Matches(msg, m.keyMap.Quit):
				return m, m.promptToQuit
			default:
				if cmd := m.keyBindingController.LookupCustomBinding(msg.String()); cmd != nil {
					return m, cmd
				}
			}
		}
	}

	var cmd tea.Cmd
	m.root, cmd = m.root.Update(msg)
	return m, cmd
}

func (m Model) Init() tea.Cmd {
	return tea.Batch(
		m.tableReadController.Init,
		m.root.Init(),
	)
}

func (m Model) View() string {
	return m.root.View()
}

func (m *Model) setMainViewIndex(viewIndex int) tea.Cmd {
	log.Printf("setting view index = %v", viewIndex)

	var newMainView tea.Model
	switch viewIndex {
	case ViewModeTablePrimary:
		newMainView = layout.NewVBox(layout.LastChildFixedAt(14), m.tableView, m.itemView)
	case ViewModeTableItemEqual:
		newMainView = layout.NewVBox(layout.EqualSize(), m.tableView, m.itemView)
	case ViewModeItemPrimary:
		newMainView = layout.NewVBox(layout.FirstChildFixedAt(7), m.tableView, m.itemView)
	case ViewModeItemOnly:
		newMainView = layout.NewZStack(m.itemView, m.tableView)
	case ViewModeTableOnly:
		newMainView = layout.NewZStack(m.tableView, m.tableView)
	default:
		newMainView = m.mainView
	}

	m.mainViewIndex = viewIndex
	m.mainView = newMainView
	m.itemEdit.SetSubmodel(m.mainView)
	return m.tableView.Refresh()
}

func (m *Model) promptToQuit() tea.Msg {
	return events.Confirm("Quit dynamo-browse? ", func(yes bool) tea.Msg {
		if yes {
			return tea.Quit()
		}
		return nil
	})
}

func (m *Model) SelectedItemIndex() int {
	return m.tableView.SelectedItemIndex()
}

func (m *Model) SetSelectedItemIndex(newIdx int) tea.Msg {
	return m.tableView.SetSelectedItemIndex(newIdx)
}
