package modal

import (
	tea "github.com/charmbracelet/bubbletea"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/layout"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/teamodels/utils"
	"log"
)

// Modal is a tea model which displays modes on a stack.  Only the top-level model is display and will receive
// keyboard and mouse events.
type Modal struct {
	baseMode  tea.Model
	modeStack []tea.Model
}

func New(baseMode tea.Model) Modal {
	return Modal{baseMode: baseMode}
}

func (m Modal) Init() tea.Cmd {
	return m.baseMode.Init()
}

// Push pushes a new model onto the modal stack
func (m *Modal) Push(model tea.Model) {
	m.modeStack = append(m.modeStack, model)
	log.Printf("pusing new mode: len = %v", len(m.modeStack))
}

// Pop pops a model from the stack
func (m *Modal) Pop() (p tea.Model) {
	if len(m.modeStack) > 0 {
		p = m.modeStack[len(m.modeStack)-1]
		m.modeStack = m.modeStack[:len(m.modeStack)-1]
		return p
	}
	return nil
}

// Len returns the number of models on the mode stack
func (m Modal) Len() int {
	return len(m.modeStack)
}

func (m Modal) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cc utils.CmdCollector

	switch msg := msg.(type) {
	case tea.KeyMsg, tea.MouseMsg:
		// only notify top level stack
		if len(m.modeStack) > 0 {
			m.modeStack[len(m.modeStack)-1] = cc.Collect(m.modeStack[len(m.modeStack)-1].Update(msg)).(tea.Model)
		} else {
			m.baseMode = cc.Collect(m.baseMode.Update(msg)).(tea.Model)
		}
	default:
		// notify all modes of other events
		// TODO: is this right?
		m.baseMode = cc.Collect(m.baseMode.Update(msg)).(tea.Model)
		for i, s := range m.modeStack {
			m.modeStack[i] = cc.Collect(s.Update(msg)).(tea.Model)
		}
	}

	return m, cc.Cmd()
}

func (m Modal) View() string {
	// only show top level mode
	if len(m.modeStack) > 0 {
		log.Printf("viewing mode stack: len = %v", len(m.modeStack))
		return m.modeStack[len(m.modeStack)-1].View()
	}
	return m.baseMode.View()
}

func (m Modal) Resize(w, h int) layout.ResizingModel {
	m.baseMode = layout.Resize(m.baseMode, w, h)
	for i := range m.modeStack {
		m.modeStack[i] = layout.Resize(m.modeStack[i], w, h)
	}
	return m
}
