package app

import (
	"strings"

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

const (
	projectProfileFieldAbout       = "about"
	projectProfileFieldConventions = "conventions"
)

// openProjectProfile opens the project profile editor for the selected session's bound workspace.
func (m *Model) openProjectProfile() {
	s := m.session(m.selectedID)
	if s == nil {
		return
	}
	m.overlay = OverlayProjectProfile
	m.projectProfileIndex = 0
	m.projectProfileEditing = false
	m.projectProfileEditBuf = ""
	m.projectProfileError = ""
	m.projectProfileStaged = map[string]string{}
	m.projectProfileLoading = true
	m.client.Send(protocol.GetProjectProfile(s.ID))
}

// applyProjectProfileSnapshot rebuilds the editable field list from a project_profile.snapshot.
func (m *Model) applyProjectProfileSnapshot(about string, conventions []string, commands map[string]string, errMsg string) {
	m.projectProfileLoading = false
	m.projectProfileError = errMsg
	if errMsg != "" {
		return
	}
	fields := []ProfileField{
		{Key: projectProfileFieldAbout, Value: about},
		{Key: projectProfileFieldConventions, Value: strings.Join(conventions, "; ")},
	}
	for k, v := range commands {
		fields = append(fields, ProfileField{Key: k, Value: v})
	}
	m.projectProfileFields = fields
	m.projectProfileStaged = map[string]string{}
}

// handleProjectProfileKey owns every key while the project profile overlay is open.
func (m Model) handleProjectProfileKey(k keyMsg) (tea.Model, tea.Cmd) {
	if m.projectProfileEditing {
		switch k.Type {
		case keyEsc:
			m.projectProfileEditing = false
			m.projectProfileEditBuf = ""
		case keyEnter:
			if m.projectProfileIndex >= 0 && m.projectProfileIndex < len(m.projectProfileFields) {
				m.projectProfileStaged[m.projectProfileFields[m.projectProfileIndex].Key] = m.projectProfileEditBuf
			}
			m.projectProfileEditing = false
			m.projectProfileEditBuf = ""
		case keyBackspace:
			if n := len(m.projectProfileEditBuf); n > 0 {
				m.projectProfileEditBuf = m.projectProfileEditBuf[:n-1]
			}
		case keyRunes, keySpace:
			m.projectProfileEditBuf += string(k.Runes)
		}
		return m, nil
	}

	switch {
	case k.Type == keyEsc || runeIs(k, "p"):
		m.overlay = OverlayNone
	case k.Type == keyUp || runeIs(k, "k"):
		if m.projectProfileIndex > 0 {
			m.projectProfileIndex--
		}
	case k.Type == keyDown || runeIs(k, "j"):
		if m.projectProfileIndex < len(m.projectProfileFields)-1 {
			m.projectProfileIndex++
		}
	case k.Type == keyEnter || k.Type == keySpace:
		if m.projectProfileIndex >= 0 && m.projectProfileIndex < len(m.projectProfileFields) {
			m.projectProfileEditing = true
			m.projectProfileEditBuf = m.currentProjectProfileValue(m.projectProfileFields[m.projectProfileIndex])
		}
	case runeIs(k, "s"):
		if len(m.projectProfileStaged) > 0 {
			m = m.saveProjectProfile()
		}
	}
	return m, nil
}

// saveProjectProfile merges staged edits into the field list and sends the whole profile back.
func (m Model) saveProjectProfile() Model {
	s := m.session(m.selectedID)
	if s == nil {
		return m
	}
	about := ""
	conventions := []string{}
	commands := map[string]string{}
	for _, f := range m.projectProfileFields {
		v := m.currentProjectProfileValue(f)
		switch f.Key {
		case projectProfileFieldAbout:
			about = v
		case projectProfileFieldConventions:
			for _, c := range strings.Split(v, ";") {
				c = strings.TrimSpace(c)
				if c != "" {
					conventions = append(conventions, c)
				}
			}
		default:
			if v != "" {
				commands[f.Key] = v
			}
		}
	}
	m.client.Send(protocol.UpdateProjectProfile(s.ID, about, conventions, commands))
	return m
}

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

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

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

	if m.projectProfileLoading && len(m.projectProfileFields) == 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.projectProfileError != "" {
		b.WriteString(lipgloss.NewStyle().Foreground(t.P.Bad).Background(t.P.BgPanel).Render("  ▲ "+truncate(m.projectProfileError, w-8)) + "\n\n")
	}

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

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

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

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

	var valStr string
	if m.projectProfileEditing && i == m.projectProfileIndex {
		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.projectProfileEditBuf) + caret
	} else {
		valFg := t.P.Accent2
		if staged {
			valFg = t.P.Warn
		}
		val := m.currentProjectProfileValue(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
}
