package render

import (
	"path/filepath"
	"strings"

	"github.com/crevissepartners/projmux/internal/core/aibadge"
	"github.com/crevissepartners/projmux/internal/theme"
	"github.com/crevissepartners/projmux/internal/ui/picker"
)

const (
	ansiReset = theme.ANSIReset
	ansiBold  = theme.ANSIBold
	ansiDim   = theme.ANSIDim
)

// ANSI role escapes consumed by the switch list renderer. They default to the
// historical fallback literals (see internal/theme/palette.go) so a fallback
// theme renders byte-identically. ApplyTheme repoints them at a resolved
// effective theme so an explicit global theme repaints the switch surface.
//
// These are package-level vars rather than threaded parameters because the
// switch formatter functions are called from 30+ stateless call sites; a
// parameter thread would be far more invasive. The switch/recent CLI render is
// single-threaded so there is no data race: ApplyTheme runs once at command
// entry before any render.
var (
	ansiRed      = theme.ANSIStateTaggedStart
	ansiBlue     = theme.ANSIStateInfoStart
	ansiGreen    = theme.ANSIStateExistingStart
	ansiYellow   = theme.ANSIStatePinnedStart
	ansiProgress = theme.ANSIStateProgressStart
	ansiCyan     = theme.ANSIAccentSettingsStart
	ansiWarning  = theme.ANSIAIBadgeActionRequiredStart

	ansiStatusPath        = theme.ANSISwitchPathStart
	ansiStatusGitActive   = theme.ANSISwitchGitActiveStart
	ansiStatusGitInactive = theme.ANSISwitchGitInactiveStart
	ansiTabActive         = theme.ANSISwitchWindowTabActiveStart
	ansiTabInactive       = theme.ANSISwitchWindowTabStart

	ansiAIBadgeSuccess        = theme.ANSIAIBadgeSuccessStart
	ansiAIBadgeActionRequired = theme.ANSIAIBadgeActionRequiredStart
	ansiAIBadgeProgress       = theme.ANSIAIBadgeProgressStart
	ansiAttentionNeeds        = theme.ANSISwitchAttentionNeedsStart
	ansiAttentionReady        = theme.ANSISwitchAttentionReadyStart
)

// ApplyTheme repoints the switch renderer's ANSI role escapes at a resolved
// effective theme. It must be called once at command entry, before any switch
// row is formatted. Passing a fallback-sourced ANSIRoles restores byte-identity.
func ApplyTheme(roles theme.ANSIRoles) {
	ansiRed = roles.StateTagged
	ansiBlue = roles.StateInfo
	ansiGreen = roles.StateExisting
	ansiYellow = roles.StatePinned
	ansiProgress = roles.StateProgress
	ansiCyan = roles.AccentSettings
	ansiWarning = roles.AIBadgeActionRequired

	ansiStatusPath = roles.SwitchPath
	ansiStatusGitActive = roles.SwitchGitActive
	ansiStatusGitInactive = roles.SwitchGitInactive
	ansiTabActive = roles.SwitchWindowTabActive
	ansiTabInactive = roles.SwitchWindowTab

	ansiAIBadgeSuccess = roles.AIBadgeSuccess
	ansiAIBadgeActionRequired = roles.AIBadgeActionRequired
	ansiAIBadgeProgress = roles.AIBadgeProgress
	ansiAttentionNeeds = roles.SwitchAttentionNeeds
	ansiAttentionReady = roles.SwitchAttentionReady
}

const (
	switchBranchBadgeMax     = 16
	switchWindowTabNameWidth = 8
	switchSidebarTabSlots    = 3
)

type SwitchRow struct {
	Label string
	Value string
	Item  picker.Item
}

type SwitchCandidate struct {
	Path          string
	DisplayPath   string
	DisplayName   string
	SessionName   string
	ModeLabel     string
	GitBranch     string
	WindowTabs    []SwitchWindowTab
	UI            string
	AttentionRank int
	AIBadgeKind   string
	AIBadgeStyle  string
	Pinned        bool
	Tagged        bool
}

type SwitchWindowTab struct {
	Name          string
	AttentionRank int
	AIBadgeKind   string
	AIBadgeStyle  string
	Active        bool
}

func PrettyPath(path, homeDir, repoRoot string) string {
	path = strings.TrimSpace(path)
	if path == "" {
		return ""
	}

	path = filepath.Clean(path)
	homeDir = cleanPrettyRoot(homeDir)
	repoRoot = cleanPrettyRoot(repoRoot)

	if repoRoot != "" {
		if path == repoRoot {
			return "~rp"
		}
		if strings.HasPrefix(path, repoRoot+string(filepath.Separator)) {
			return "~rp" + strings.TrimPrefix(path, repoRoot)
		}
	}

	if homeDir != "" {
		if path == homeDir {
			return "~"
		}
		if strings.HasPrefix(path, homeDir+string(filepath.Separator)) {
			return "~" + strings.TrimPrefix(path, homeDir)
		}
	}

	return path
}

func BuildSwitchRows(candidates []SwitchCandidate) []SwitchRow {
	rows := make([]SwitchRow, 0, len(candidates))
	for _, candidate := range candidates {
		label := formatSwitchLabel(candidate)
		item := switchPickerItem(candidate)
		if candidate.UI == "sidebar" {
			item.Label = label
			item.MetaLines = nil
		}
		rows = append(rows, SwitchRow{
			Label: label,
			Value: candidate.Path,
			Item:  item,
		})
	}
	return rows
}

func BuildSwitchPickerItems(candidates []SwitchCandidate) []picker.Item {
	items := make([]picker.Item, 0, len(candidates))
	for _, candidate := range candidates {
		items = append(items, switchPickerItem(candidate))
	}
	return items
}

func FormatSwitchCardLabel(item picker.Item) string {
	lines := []string{formatSwitchCardTitle(item)}
	for _, meta := range sanitizeCells(item.MetaLines) {
		if meta == "" {
			continue
		}
		lines = append(lines, formatSwitchCardMetaLine(meta))
	}
	return strings.Join(lines, "\n")
}

func switchPickerItem(candidate SwitchCandidate) picker.Item {
	title := sanitizeCell(candidate.DisplayName)
	if title == "" {
		title = sanitizeCell(candidate.SessionName)
	}
	if candidate.Path == "__projmux_settings__" {
		title = sanitizeCell(candidate.DisplayPath)
		if title == "" {
			title = "Settings"
		}
	}

	metaLines := make([]string, 0, 3)
	if meta := formatSwitchPathGitLine(candidate); meta != "" {
		metaLines = append(metaLines, meta)
	}
	if windows := formatSwitchWindowTabs(candidate.WindowTabs, candidate.AIBadgeStyle); windows != "" {
		metaLines = append(metaLines, windows)
	}

	badges := make([]string, 0, 3)
	if badge := switchAttentionBadgeName(candidate.AIBadgeKind, candidate.AttentionRank); badge != "" {
		badges = append(badges, badge)
	} else if candidate.AttentionRank == 2 {
		badges = append(badges, "needs review")
	} else if candidate.AttentionRank == 1 {
		badges = append(badges, "ready")
	}
	if candidate.Tagged {
		badges = append(badges, "tagged")
	}
	if candidate.Pinned {
		badges = append(badges, "pinned")
	}

	return picker.Item{
		Title:         title,
		Value:         candidate.Path,
		State:         sanitizeCell(candidate.ModeLabel),
		SearchText:    title,
		MetaLines:     metaLines,
		Badges:        badges,
		PreviewTarget: candidate.Path,
	}
}

func formatSwitchCardMetaLine(meta string) string {
	if strings.Contains(meta, "\x1b[") {
		return "  " + meta
	}
	return ansiDim + "  " + meta + ansiReset
}

func sanitizeCells(values []string) []string {
	cells := make([]string, 0, len(values))
	for _, value := range values {
		value = sanitizeCell(value)
		if value == "" {
			continue
		}
		cells = append(cells, value)
	}
	return cells
}

func formatSwitchPathGitLine(candidate SwitchCandidate) string {
	path := switchPickerPath(candidate)
	branch := truncateSwitchBadge(sanitizeCell(candidate.GitBranch), switchBranchBadgeMax)
	if path == "" && branch == "" {
		return ""
	}
	parts := make([]string, 0, 2)
	if path != "" {
		parts = append(parts, ansiStatusPath+path+ansiReset)
	}
	if branch != "" {
		style := ansiStatusGitInactive
		if candidate.ModeLabel == "existing" {
			style = ansiStatusGitActive
		}
		parts = append(parts, style+" "+branch+" "+ansiReset)
	}
	return strings.Join(parts, " ")
}

func truncateSwitchBadge(value string, max int) string {
	if max <= 0 {
		return ""
	}
	runes := []rune(value)
	if len(runes) <= max {
		return value
	}
	if max <= 3 {
		return string(runes[:max])
	}
	return string(runes[:max-3]) + "..."
}

func formatSwitchWindowTabs(windows []SwitchWindowTab, badgeStyle string) string {
	tabs := make([]string, 0, len(windows))
	for _, window := range windows {
		name := sanitizeCell(window.Name)
		if name == "" {
			continue
		}
		style := window.AIBadgeStyle
		if strings.TrimSpace(style) == "" {
			style = badgeStyle
		}
		tabs = append(tabs, formatSwitchWindowTab(name, window.AIBadgeKind, window.AttentionRank, style, window.Active))
	}
	return strings.Join(tabs, " ")
}

func formatSwitchWindowTab(name, badgeKind string, attentionRank int, badgeStyle string, active bool) string {
	style := ansiTabInactive
	if active {
		style = ansiTabActive
	}
	badge := formatInlineAttentionBadge(badgeKind, attentionRank, badgeStyle)
	if badge != "" {
		badge += style
	}
	return style + " " + badge + centerSwitchTabName(name, switchWindowTabNameWidth) + " " + ansiReset
}

func formatSidebarSwitchWindowTabs(windows []SwitchWindowTab, badgeStyle string) string {
	tabs := make([]string, 0, switchSidebarTabSlots)
	for _, window := range windows {
		if len(tabs) >= switchSidebarTabSlots {
			break
		}
		name := sanitizeCell(window.Name)
		if name == "" {
			continue
		}
		style := window.AIBadgeStyle
		if strings.TrimSpace(style) == "" {
			style = badgeStyle
		}
		tabs = append(tabs, formatSidebarSwitchWindowTab(name, window.AIBadgeKind, window.AttentionRank, style, window.Active))
	}
	for len(tabs) < switchSidebarTabSlots {
		tabs = append(tabs, formatSidebarBlankLane(sidebarSwitchWindowTabWidth()))
	}
	return strings.Join(tabs, " ")
}

func formatSidebarSwitchWindowTab(name, badgeKind string, attentionRank int, badgeStyle string, active bool) string {
	style := ansiTabInactive
	if active {
		style = ansiTabActive
	}
	badge := formatInlineAttentionBadge(badgeKind, attentionRank, badgeStyle)
	if badge == "" {
		badge = "  "
	} else {
		badge += style
	}
	return style + " " + badge + centerSwitchTabName(name, switchWindowTabNameWidth) + " " + ansiReset
}

func sidebarSwitchWindowTabWidth() int {
	return 1 + 2 + switchWindowTabNameWidth + 1
}

func centerSwitchTabName(name string, width int) string {
	name = truncateSwitchBadge(name, width)
	runes := []rune(name)
	if width <= 0 || len(runes) >= width {
		return name
	}
	left := (width - len(runes)) / 2
	right := width - len(runes) - left
	return strings.Repeat(" ", left) + name + strings.Repeat(" ", right)
}

func formatSwitchCardTitle(item picker.Item) string {
	title := sanitizeCell(item.Title)
	if title == "" {
		title = sanitizeCell(item.Value)
	}
	title = formatSwitchCardTitleText(title, item.State, item.Value)
	if badge := formatSwitchCardStatusBadge(item.Badges); badge != "" {
		title += " " + badge
	}
	return title
}

func formatSwitchCardTitleText(title, state, value string) string {
	switch {
	case value == "__projmux_settings__":
		return ansiBold + ansiCyan + title + ansiReset
	case state == "existing":
		return ansiBold + ansiGreen + title + ansiReset
	default:
		return ansiBold + title + ansiReset
	}
}

func formatSwitchCardStatusBadge(badges []string) string {
	parts := make([]string, 0, len(badges))
	for _, badge := range badges {
		switch sanitizeCell(badge) {
		case "needs input":
			parts = append(parts, ansiWarning+"●"+ansiReset)
		case "working":
			parts = append(parts, ansiProgress+"●"+ansiReset)
		case "needs review":
			parts = append(parts, ansiProgress+"●"+ansiReset)
		case "ready":
			parts = append(parts, ansiAIBadgeSuccess+"●"+ansiReset)
		case "tagged":
			parts = append(parts, ansiRed+"x"+ansiReset)
		case "pinned":
			parts = append(parts, ansiYellow+"*"+ansiReset)
		}
	}
	return strings.Join(parts, " ")
}

func formatInlineAttentionBadge(kind string, rank int, badgeStyle string) string {
	if style := ansiAIBadgeKindStart(kind); style != "" {
		glyph := aibadge.Glyph(kind, badgeStyle)
		if strings.TrimSpace(glyph) == "" {
			return "  "
		}
		return style + glyph + " " + ansiReset
	}
	switch rank {
	case 2:
		return ansiAttentionNeeds + "● " + ansiReset
	case 1:
		return ansiAttentionReady + "● " + ansiReset
	default:
		return ""
	}
}

func switchPickerPath(candidate SwitchCandidate) string {
	path := sanitizeCell(candidate.DisplayPath)
	if path == "" {
		path = sanitizeCell(candidate.Path)
	}
	return path
}

func formatSwitchLabel(candidate SwitchCandidate) string {
	if candidate.Path == "__projmux_settings__" {
		return formatSettingsLabel(candidate)
	}
	if candidate.UI == "sidebar" {
		return formatSidebarSwitchLabel(candidate)
	}

	return formatPopupSwitchLabel(candidate)
}

func formatPopupSwitchLabel(candidate SwitchCandidate) string {
	parts := make([]string, 0, 5)
	parts = append(parts, formatTagSlot(candidate.Tagged))
	parts = append(parts, formatPinBadge(candidate.Pinned))

	modeLabel := formatPopupModeLabel(candidate.ModeLabel)
	if modeLabel != "" {
		parts = append(parts, modeLabel)
	}

	displayName := sanitizeCell(candidate.DisplayName)
	if displayName == "" {
		displayName = sanitizeCell(candidate.SessionName)
	}
	if displayName != "" {
		parts = append(parts, displayName)
	}

	path := sanitizeCell(candidate.DisplayPath)
	if path == "" {
		path = sanitizeCell(candidate.Path)
	}
	if path != "" {
		parts = append(parts, path)
	}

	return strings.Join(parts, "  ")
}

func formatSettingsLabel(candidate SwitchCandidate) string {
	label := sanitizeCell(candidate.DisplayPath)
	if label == "" {
		label = "Settings"
	}
	if candidate.UI != "sidebar" {
		return formatTagSlot(false) + "   " + ansiBold + ansiCyan + "[Settings]" + ansiReset + "        " + ansiDim + "manage pinned directories" + ansiReset
	}
	description := "manage pinned directories"
	return "  " + ansiBold + ansiCyan + label + ansiReset + "  " + ansiDim + description + ansiReset
}

func formatPopupModeLabel(mode string) string {
	mode = sanitizeCell(mode)
	switch mode {
	case "existing":
		return ansiGreen + "[Existing]" + ansiReset
	case "new":
		return ansiYellow + "[New]" + ansiReset
	default:
		if mode == "" {
			return ""
		}
		return "[" + mode + "]"
	}
}

func formatSidebarSwitchLabel(candidate SwitchCandidate) string {
	displayName := sanitizeCell(candidate.DisplayName)
	if displayName == "" {
		displayName = sanitizeCell(candidate.SessionName)
	}
	title := formatSidebarSessionName(displayName, candidate.ModeLabel)
	if title == "" {
		title = formatSidebarSessionName(sanitizeCell(candidate.Path), candidate.ModeLabel)
	}

	path := sanitizeCell(candidate.DisplayPath)
	if path == "" {
		path = sanitizeCell(candidate.Path)
	}
	lines := []string{
		title + " " + formatSidebarStatusLane(candidate),
	}
	if path != "" {
		lines = append(lines, ansiDim+path+ansiReset+" "+formatSidebarBranchLane(candidate))
	} else {
		lines = append(lines, formatSidebarBranchLane(candidate))
	}
	lines = append(lines, formatSidebarSwitchWindowTabs(candidate.WindowTabs, candidate.AIBadgeStyle))
	return strings.Join(lines, "\n")
}

func formatAttentionBadge(kind string, rank int) string {
	return formatAttentionBadgeWithStyle(kind, rank, aibadge.StyleDot)
}

func formatAttentionBadgeWithStyle(kind string, rank int, badgeStyle string) string {
	if style := ansiAIBadgeKindStart(kind); style != "" {
		glyph := aibadge.Glyph(kind, badgeStyle)
		if strings.TrimSpace(glyph) == "" {
			return " "
		}
		return style + glyph + ansiReset
	}
	switch rank {
	case 2:
		return ansiProgress + "●" + ansiReset
	case 1:
		return ansiGreen + "●" + ansiReset
	default:
		return " "
	}
}

func formatSidebarSessionName(sessionName, mode string) string {
	mode = sanitizeCell(mode)
	switch mode {
	case "existing":
		return ansiBold + ansiGreen + sessionName + ansiReset
	case "new":
		return sessionName
	default:
		return sessionName
	}
}

func formatSidebarStatusLane(candidate SwitchCandidate) string {
	return strings.Join([]string{
		formatAttentionBadgeWithStyle(candidate.AIBadgeKind, candidate.AttentionRank, candidate.AIBadgeStyle),
		formatTagBadge(candidate.Tagged),
		formatPinBadge(candidate.Pinned),
	}, " ")
}

func switchAttentionBadgeName(kind string, rank int) string {
	switch aibadge.Normalize(kind) {
	case aibadge.ApprovalRequired, aibadge.InputRequired:
		return "needs input"
	case aibadge.ResponseComplete:
		return "ready"
	case aibadge.InProgress:
		return "working"
	default:
		if rank == 2 {
			return "needs review"
		}
		if rank == 1 {
			return "ready"
		}
		return ""
	}
}

func ansiAIBadgeKindStart(kind string) string {
	switch aibadge.ThemeRole(kind) {
	case aibadge.RoleActionRequired:
		return ansiAIBadgeActionRequired
	case aibadge.RoleSuccess:
		return ansiAIBadgeSuccess
	case aibadge.RoleProgress:
		return ansiAIBadgeProgress
	default:
		return ""
	}
}

func formatSidebarBranchLane(candidate SwitchCandidate) string {
	branch := truncateSwitchBadge(sanitizeCell(candidate.GitBranch), switchBranchBadgeMax)
	style := ansiStatusGitInactive
	if candidate.ModeLabel == "existing" {
		style = ansiStatusGitActive
	}
	if branch == "" {
		return formatSidebarBlankLane(switchBranchBadgeMax + 2)
	}
	return style + " " + branch + " " + ansiReset + formatSidebarBlankLane(switchBranchBadgeMax-len([]rune(branch)))
}

func formatSidebarBlankLane(width int) string {
	if width <= 0 {
		return ""
	}
	return ansiReset + strings.Repeat(" ", width) + ansiReset
}

func formatTagBadge(tagged bool) string {
	if tagged {
		return ansiRed + "x" + ansiReset
	}
	return " "
}

func formatTagSlot(tagged bool) string {
	if tagged {
		return "[" + ansiRed + "x" + ansiReset + "]"
	}
	return "[ ]"
}

func formatPinBadge(pinned bool) string {
	if pinned {
		return ansiYellow + "*" + ansiReset
	}
	return " "
}

func sanitizeCell(value string) string {
	value = strings.ReplaceAll(value, "\t", " ")
	value = strings.ReplaceAll(value, "\n", " ")
	return strings.TrimSpace(value)
}

func cleanPrettyRoot(path string) string {
	if strings.TrimSpace(path) == "" {
		return ""
	}

	return filepath.Clean(path)
}
