package app

import (
	"strings"

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

const (
	operatorProfileFieldAbout           = "about"
	operatorProfileFieldApprovalMode    = "approvalMode"
	operatorProfileFieldPreferredModels = "preferredModels"
	operatorProfileFieldConventions     = "conventions"
)

// openOperatorProfile opens the operator profile editor (home-scoped, not session-bound).
func (m *Model) openOperatorProfile() {
	m.overlay = OverlayOperatorProfile
	m.operatorProfileIndex = 0
	m.operatorProfileEditing = false
	m.operatorProfileEditBuf = ""
	m.operatorProfileError = ""
	m.operatorProfileStaged = map[string]string{}
	m.operatorProfileLoading = true
	m.client.Send(protocol.GetOperatorProfile())
}

// applyOperatorProfileSnapshot rebuilds the editable field list from an operator_profile.snapshot.
func (m *Model) applyOperatorProfileSnapshot(about, approvalMode string, preferredModels, conventions []string, proposed, errMsg string) {
	m.operatorProfileLoading = false
	m.operatorProfileError = errMsg
	m.operatorProfileProposed = proposed
	if errMsg != "" {
		return
	}
	m.operatorProfileFields = []ProfileField{
		{Key: operatorProfileFieldAbout, Value: about},
		{Key: operatorProfileFieldApprovalMode, Value: approvalMode},
		{Key: operatorProfileFieldPreferredModels, Value: strings.Join(preferredModels, ", ")},
		{Key: operatorProfileFieldConventions, Value: strings.Join(conventions, "; ")},
	}
	m.operatorProfileStaged = map[string]string{}
}

// handleOperatorProfileKey owns every key while the operator profile overlay is open.
func (m Model) handleOperatorProfileKey(k keyMsg) (tea.Model, tea.Cmd) {
	if m.operatorProfileEditing {
		switch k.Type {
		case keyEsc:
			m.operatorProfileEditing = false
			m.operatorProfileEditBuf = ""
		case keyEnter:
			if m.operatorProfileIndex >= 0 && m.operatorProfileIndex < len(m.operatorProfileFields) {
				m.operatorProfileStaged[m.operatorProfileFields[m.operatorProfileIndex].Key] = m.operatorProfileEditBuf
			}
			m.operatorProfileEditing = false
			m.operatorProfileEditBuf = ""
		case keyBackspace:
			if n := len(m.operatorProfileEditBuf); n > 0 {
				m.operatorProfileEditBuf = m.operatorProfileEditBuf[:n-1]
			}
		case keyRunes, keySpace:
			m.operatorProfileEditBuf += string(k.Runes)
		}
		return m, nil
	}

	switch {
	case k.Type == keyEsc || runeIs(k, "o"):
		m.overlay = OverlayNone
	case k.Type == keyUp || runeIs(k, "k"):
		if m.operatorProfileIndex > 0 {
			m.operatorProfileIndex--
		}
	case k.Type == keyDown || runeIs(k, "j"):
		if m.operatorProfileIndex < len(m.operatorProfileFields)-1 {
			m.operatorProfileIndex++
		}
	case k.Type == keyEnter || k.Type == keySpace:
		if m.operatorProfileIndex >= 0 && m.operatorProfileIndex < len(m.operatorProfileFields) {
			m.operatorProfileEditing = true
			m.operatorProfileEditBuf = m.currentOperatorProfileValue(m.operatorProfileFields[m.operatorProfileIndex])
		}
	case runeIs(k, "s"):
		if len(m.operatorProfileStaged) > 0 {
			m = m.saveOperatorProfile()
		}
	}
	return m, nil
}

// saveOperatorProfile merges staged edits into the field list and sends the whole profile back.
func (m Model) saveOperatorProfile() Model {
	var about, approvalMode string
	var preferredModels, conventions []string
	for _, f := range m.operatorProfileFields {
		v := m.currentOperatorProfileValue(f)
		switch f.Key {
		case operatorProfileFieldAbout:
			about = v
		case operatorProfileFieldApprovalMode:
			approvalMode = v
		case operatorProfileFieldPreferredModels:
			preferredModels = splitTrimmed(v, ",")
		case operatorProfileFieldConventions:
			conventions = splitTrimmed(v, ";")
		}
	}
	m.client.Send(protocol.UpdateOperatorProfile(about, approvalMode, preferredModels, conventions))
	return m
}

func splitTrimmed(s, sep string) []string {
	out := []string{}
	for _, p := range strings.Split(s, sep) {
		p = strings.TrimSpace(p)
		if p != "" {
			out = append(out, p)
		}
	}
	return out
}

// currentOperatorProfileValue returns the staged edit for a field if present, else its server value.
func (m Model) currentOperatorProfileValue(f ProfileField) string {
	if v, ok := m.operatorProfileStaged[f.Key]; ok {
		return v
	}
	return f.Value
}

func (m Model) operatorProfileModal() string {
	t := m.theme
	w := m.modalWidth()

	var b strings.Builder
	b.WriteString(m.titleLine("operator profile"))
	if n := len(m.operatorProfileStaged); n > 0 {
		b.WriteString(mbg(t, "   ("+itoa(n)+" unsaved)", t.P.Warn))
	}
	b.WriteString("\n\n")

	if m.operatorProfileLoading && len(m.operatorProfileFields) == 0 {
		b.WriteString(mbg(t, "  loading…", t.P.Faint) + "\n")
		b.WriteString("\n" + modalHints(t, [][2]string{{"esc", "close"}}))
		return t.Overlay.Width(w).Render(b.String())
	}
	if m.operatorProfileError != "" {
		b.WriteString(lipgloss.NewStyle().Foreground(t.P.Bad).Background(t.P.BgPanel).Render("  ▲ "+truncate(m.operatorProfileError, w-8)) + "\n\n")
	}

	for i, f := range m.operatorProfileFields {
		b.WriteString(m.operatorProfileRow(i, f) + "\n")
	}

	if m.operatorProfileProposed != "" {
		b.WriteString("\n" + mbg(t, "  proposed adaptation (review + merge by hand):", t.P.Warn) + "\n")
		for _, ln := range wrapLine(m.operatorProfileProposed, w-6) {
			b.WriteString(lipgloss.NewStyle().Foreground(t.P.Dim).Background(t.P.BgPanel).Render("  "+ln) + "\n")
		}
	}

	b.WriteString("\n" + modalHints(t, [][2]string{
		{"↑↓", "select"}, {"enter", "edit"}, {"s", "save"}, {"o/esc", "close"},
	}))
	return t.Overlay.Width(w).Render(b.String())
}

func (m Model) operatorProfileRow(i int, f ProfileField) string {
	t := m.theme
	_, staged := m.operatorProfileStaged[f.Key]

	marker := mbg(t, "  ", t.P.BgPanel)
	keyFg := t.P.Fg
	if i == m.operatorProfileIndex {
		marker = lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.BgPanel).Render("▸ ")
		keyFg = t.P.FgStrong
	}

	var valStr string
	if m.operatorProfileEditing && i == m.operatorProfileIndex {
		caret := lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.BgPanel).Render("▏")
		valStr = lipgloss.NewStyle().Foreground(t.P.FgStrong).Background(t.P.BgPanel).Render(m.operatorProfileEditBuf) + caret
	} else {
		valFg := t.P.Accent2
		if staged {
			valFg = t.P.Warn
		}
		val := m.currentOperatorProfileValue(f)
		if staged {
			val = "*" + val
		}
		valStr = lipgloss.NewStyle().Foreground(valFg).Background(t.P.BgPanel).Render(val)
	}

	return marker + lipgloss.NewStyle().Foreground(keyFg).Background(t.P.BgPanel).Render(padRaw(f.Key, 20)) + " " + valStr
}
