package app

import (
	"strings"

	tea "charm.land/bubbletea/v2"
	"charm.land/lipgloss/v2"
	"github.com/correx/tui-go/internal/protocol"
)

// proposecard.go renders and drives the workflow-propose view: the router's triage
// suggestion of candidate workflows, presented as a single-select picker with a
// manual-answer slot (like a choice panel). Picking a candidate launches that
// workflow seeded with the original request; the custom slot continues the chat.

// --- widget state ---

func (m *Model) proposeInitState() {
	m.proposeCursor = 0
	m.proposeText = ""
	m.proposeTyping = false
	m.proposeDismissed = false
}

// --- key handling ---

func (m Model) handleProposeKey(k keyMsg) (tea.Model, tea.Cmd) {
	s := m.session(m.selectedID)
	if s == nil || s.Propose == nil {
		return m, nil
	}
	if m.proposeTyping {
		return m.handleProposeTyping(k)
	}
	n := len(s.Propose.Candidates) // index n == the custom slot
	switch k.Type {
	case keyEsc:
		m.proposeDismissed = true
	case keyUp:
		m.proposeCursor = clampInt(m.proposeCursor-1, 0, n)
	case keyDown:
		m.proposeCursor = clampInt(m.proposeCursor+1, 0, n)
	case keyEnter, keySpace:
		return m.proposeSubmit()
	case keyRunes:
		switch string(k.Runes) {
		case "k":
			m.proposeCursor = clampInt(m.proposeCursor-1, 0, n)
		case "j":
			m.proposeCursor = clampInt(m.proposeCursor+1, 0, n)
		case "e":
			m.proposeCursor = n // jump to the custom slot and start typing
			m.proposeTyping = true
		}
	}
	return m, nil
}

func (m Model) handleProposeTyping(k keyMsg) (tea.Model, tea.Cmd) {
	switch k.Type {
	case keyEsc:
		m.proposeTyping = false
	case keyEnter:
		return m.proposeSubmit() // commit the custom answer and send it as chat
	case keyBackspace:
		if len(m.proposeText) > 0 {
			m.proposeText = m.proposeText[:len(m.proposeText)-1]
		}
	case keyRunes, keySpace:
		m.proposeText += string(k.Runes)
	}
	return m, nil
}

// proposeSubmit acts on the focused row: a candidate launches its workflow (seeded
// with the original request); the custom slot continues the conversation as a chat
// turn. An empty custom slot just drops into typing rather than submitting nothing.
func (m Model) proposeSubmit() (tea.Model, tea.Cmd) {
	s := m.session(m.selectedID)
	if s == nil || s.Propose == nil {
		return m, nil
	}
	p := s.Propose
	if m.proposeCursor >= len(p.Candidates) {
		text := strings.TrimSpace(m.proposeText)
		if text == "" {
			m.proposeTyping = true
			return m, nil
		}
		m.client.Send(protocol.ChatInput(p.SessionID, text, "CHAT"))
		s.Propose = nil
		m.proposeInitState()
		return m, nil
	}
	pick := p.Candidates[m.proposeCursor]
	m.client.Send(protocol.StartSession(pick.WorkflowID, p.OriginalRequest))
	m.pendingWorkflowFocus = true // focus the launched session when it announces
	s.Propose = nil
	m.proposeInitState()
	return m, nil
}

// --- rendering ---

func (m Model) proposeModal() string {
	t := m.theme
	w := m.modalWidth()
	s := m.session(m.selectedID)
	if s == nil || s.Propose == nil {
		return ""
	}
	p := s.Propose

	var b strings.Builder
	b.WriteString(m.titleLine("workflow suggestion") + "\n")
	if p.OriginalRequest != "" {
		b.WriteString(mbg(t, "  for: "+p.OriginalRequest, t.P.Faint) + "\n")
	}
	if p.Prompt != "" {
		b.WriteString(mbg(t, "  "+p.Prompt, t.P.Fg) + "\n")
	}
	b.WriteString("\n")

	contentW := w - 6
	for i, c := range p.Candidates {
		b.WriteString(m.proposeRow(c, i == m.proposeCursor, contentW) + "\n")
	}
	b.WriteString(m.proposeCustomRow(m.proposeCursor >= len(p.Candidates), contentW) + "\n")

	b.WriteString("\n" + modalHints(t, [][2]string{
		{"↑↓", "choose"}, {"enter", "launch"}, {"e", "custom"}, {"esc", "later"},
	}))
	return t.Overlay.Width(w).Render(b.String())
}

// proposeRow renders one candidate workflow: a focus marker, the [workflow-id] chip,
// and its reason.
func (m Model) proposeRow(c ProposeCandidate, focused bool, w int) string {
	t := m.theme
	marker := mbg(t, "  ", t.P.BgPanel)
	reasonFg := t.P.Dim
	if focused {
		marker = lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.BgPanel).Render("▸ ")
		reasonFg = t.P.Fg
	}
	line := marker + lipgloss.NewStyle().Foreground(t.P.Accent2).Background(t.P.BgPanel).Render("["+c.WorkflowID+"]")
	if c.Reason != "" {
		line += mbg(t, " "+c.Reason, reasonFg)
	}
	return clip(line, w)
}

// proposeCustomRow renders the manual-answer slot: a prompt when empty, the typed
// text (with a caret while editing) otherwise.
func (m Model) proposeCustomRow(focused bool, w int) string {
	t := m.theme
	marker := mbg(t, "  ", t.P.BgPanel)
	fg := t.P.Dim
	if focused {
		marker = lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.BgPanel).Render("▸ ")
		fg = t.P.Fg
	}
	label := "✎ something else…"
	switch {
	case m.proposeTyping:
		label = "✎ " + m.proposeText + "▏"
	case strings.TrimSpace(m.proposeText) != "":
		label = "✎ " + m.proposeText
	}
	return clip(marker+lipgloss.NewStyle().Foreground(fg).Background(t.P.BgPanel).Render(label), w)
}
