package input

import (
	"charm.land/bubbles/v2/textinput"
	tea "charm.land/bubbletea/v2"
	"charm.land/lipgloss/v2"
	"github.com/idursun/jjui/internal/ui/actions"
	"github.com/idursun/jjui/internal/ui/common"
	"github.com/idursun/jjui/internal/ui/intents"
	"github.com/idursun/jjui/internal/ui/layout"
	"github.com/idursun/jjui/internal/ui/render"
)

type SelectedMsg struct {
	Value string
}

type CancelledMsg struct{}

var (
	_ common.ImmediateModel = (*Model)(nil)
	_ common.Focusable      = (*Model)(nil)
)

type Model struct {
	input  textinput.Model
	title  string
	prompt string
}

func (m *Model) IsFocused() bool {
	return true
}

func New() *Model {
	return NewWithTitle("", "", "")
}

func NewWithTitle(title string, prompt string, value string) *Model {
	ti := textinput.New()
	ti.SetWidth(40)
	ti.Prompt = prompt
	ti.SetVirtualCursor(false)
	if ti.Prompt == "" {
		ti.Prompt = "> "
	}
	if value != "" {
		ti.SetValue(value)
		ti.CursorEnd()
	}
	ti.Focus()

	return &Model{
		input:  ti,
		title:  title,
		prompt: prompt,
	}
}

func (m *Model) Scopes() []common.Scope {
	return []common.Scope{
		{
			Name:    actions.ScopeInput,
			Leak:    common.LeakNone,
			Handler: m,
		},
	}
}

func (m *Model) HandleIntent(intent intents.Intent) (tea.Cmd, bool) {
	switch intent.(type) {
	case intents.Apply:
		return m.selectCurrent(), true
	case intents.Cancel:
		return newCmd(CancelledMsg{}), true
	}
	return nil, false
}

func (m *Model) Init() tea.Cmd {
	return m.input.Focus()
}

func (m *Model) Update(msg tea.Msg) tea.Cmd {
	switch msg := msg.(type) {
	case intents.Intent:
		cmd, _ := m.HandleIntent(msg)
		return cmd
	case tea.KeyMsg, tea.PasteMsg:
		var cmd tea.Cmd
		m.input, cmd = m.input.Update(msg)
		return cmd
	case common.CloseViewMsg:
		return newCmd(CancelledMsg{})
	}
	return nil
}

func (m *Model) selectCurrent() tea.Cmd {
	value := m.input.Value()
	return newCmd(SelectedMsg{Value: value})
}

func (m *Model) ViewRect(dl *render.DisplayContext, box layout.Box) {
	borderStyle := common.DefaultPalette.GetBorder("input border", lipgloss.RoundedBorder())
	textStyle := common.DefaultPalette.Get("input text")
	titleStyle := common.DefaultPalette.Get("input title")
	surfaceStyle := common.DefaultPalette.Get("input")

	var rows []string
	if m.title != "" {
		rows = append(rows, titleStyle.Render(m.title))
	}
	is := m.input.Styles()
	is.Focused.Prompt = textStyle
	is.Blurred.Prompt = textStyle
	m.input.SetStyles(is)

	m.input.SetWidth(min(box.R.Dx()-2, 40))
	rows = append(rows, m.input.View())

	content := lipgloss.JoinVertical(0, rows...)
	content = borderStyle.Padding(0, 1).Render(content)
	box = box.Center(lipgloss.Size(content))
	dl.AddBackdrop(box.R, render.ZDialogs)
	dl.AddFill(box.R, ' ', surfaceStyle, render.ZDialogs)
	dl.AddDraw(box.R, content, render.ZDialogs)
	dl.AddPaint(box.R, surfaceStyle, render.ZDialogs)
	cursorY := 1
	if m.title != "" {
		cursorY++
	}
	dl.SetCursorInRect(m.input.Cursor(), box.R, 2, cursorY)
}

func newCmd(msg tea.Msg) tea.Cmd {
	return func() tea.Msg { return msg }
}

func ShowWithTitle(title string, prompt string, value string) tea.Cmd {
	return func() tea.Msg {
		return common.ShowInputMsg{Title: title, Prompt: prompt, Value: value}
	}
}
