package ui

import (
	"fmt"
	"strings"
	"time"

	"github.com/charmbracelet/lipgloss"
)

// Colors for the UI theme — Graphite + Violet palette (locked).
// These are the bootstrap defaults used before ApplyTheme runs and must
// stay in sync with ThemeDark in themes.go (the only shipped theme).
var (
	ColorPrimary   = lipgloss.Color("#9B7BFF") // Violet (mockup #7c4dff lifted)
	ColorSecondary = lipgloss.Color("#5BA8C7") // Calm Cyan
	ColorSuccess   = lipgloss.Color("#5AB97B") // Forest Green
	ColorWarning   = lipgloss.Color("#D4A24A") // Deep Amber
	ColorError     = lipgloss.Color("#D85A4A") // Coral
	ColorMuted     = lipgloss.Color("#807D75") // Warm Gray
	ColorText      = lipgloss.Color("#E8E4D8") // Warm Off-White
	ColorBg        = lipgloss.Color("#0E1116") // Warm Graphite

	// Extended semantic colors
	ColorBorder    = lipgloss.Color("#2A2C33") // Subtle Border
	ColorHighlight = lipgloss.Color("#C0AEFF") // Lavender
	ColorDim       = lipgloss.Color("#5A5852") // Deeper Warm Gray
	ColorAccent    = lipgloss.Color("#9B7BFF") // Unified with Primary (single violet)
	ColorRunning   = lipgloss.Color("#6B8AD4") // Info Blue
	ColorInfo      = lipgloss.Color("#6BAEB5") // Calm Teal

	// Modal semantic colors
	ColorContext  = lipgloss.Color("#807D75") // = Muted (warm gray)
	ColorQuestion = ColorSecondary            // Cyan
	ColorPlan     = ColorInfo                 // Teal

	// Accent palette (used sparingly for gradients, banners, named glyphs).
	// Re-pinned to harmonise with the locked graphite+violet base.
	ColorGradient1 = lipgloss.Color("#9B7BFF") // Violet (= Primary)
	ColorGradient2 = lipgloss.Color("#6B8AD4") // Indigo-ish (= Running)
	ColorGradient3 = lipgloss.Color("#5BA8C7") // Cyan (= Secondary)
	ColorGolden    = lipgloss.Color("#D4A24A") // Amber (= Warning)
	ColorRose      = lipgloss.Color("#D85A4A") // Coral (= Error)
	ColorMint      = lipgloss.Color("#5AB97B") // Forest (= Success)
	ColorLavender  = lipgloss.Color("#C0AEFF") // Lavender (= Highlight)
	ColorSlate     = lipgloss.Color("#16191F") // Surface (raised card)
)

// MessageIcons provides consistent icons for different message types
var MessageIcons = map[string]string{
	"success": "✓",
	"error":   "✗",
	"warning": "⚠",
	"info":    "ℹ",
	"hint":    "›",
	"loading": "◐",
	"done":    "✓",
	"pending": "○",
	"active":  "●",
	"skip":    "↷",
}

// ToastIcons provides icons for toast notifications by type.
var ToastIcons = map[ToastType]string{
	ToastInfo:    "ℹ",
	ToastSuccess: "✓",
	ToastWarning: "⚠",
	ToastError:   "✗",
}

// ToastColors provides colors for toast notifications by type.
var ToastColors = map[ToastType]lipgloss.Color{
	ToastInfo:    ColorInfo,
	ToastSuccess: ColorSuccess,
	ToastWarning: ColorWarning,
	ToastError:   ColorError,
}

// FilePeekIcons provides consistent icons for file peek actions.
var FilePeekIcons = map[string]string{
	"read":      "▸",
	"reading":   "▸",
	"write":     "✦",
	"created":   "✦",
	"edit":      "△",
	"editing":   "△",
	"modifying": "△",
	"delete":    "✗",
	"deleting":  "✗",
	"default":   "▸",
}

// FilePeekColors provides colors for file peek actions.
var FilePeekColors = map[string]lipgloss.Color{
	"read":      ColorSecondary,
	"reading":   ColorSecondary,
	"write":     ColorSuccess,
	"created":   ColorSuccess,
	"edit":      ColorWarning,
	"editing":   ColorWarning,
	"modifying": ColorWarning,
	"delete":    ColorError,
	"deleting":  ColorError,
}

// StatusIcons provides icons for status indicators.
var StatusIcons = map[string]string{
	"active":   "●",
	"pending":  "○",
	"success":  "✓",
	"error":    "✗",
	"warning":  "⚠",
	"info":     "ℹ",
	"waiting":  "◐",
	"thinking": "◐",
	"typing":   "○",
	"idle":     "●",
}

// StatusColors provides colors for status indicators.
var StatusColors = map[string]lipgloss.Color{
	"active":   ColorRunning,
	"pending":  ColorMuted,
	"success":  ColorSuccess,
	"error":    ColorError,
	"warning":  ColorWarning,
	"info":     ColorInfo,
	"waiting":  ColorWarning,
	"thinking": ColorPrimary,
	"typing":   ColorSuccess,
	"idle":     ColorMuted,
}

// ToolIcons provides minimal Unicode glyphs for tool calls — no emoji, consistent rendering.
var ToolIcons = map[string]string{
	"read":         "▸",
	"write":        "✦",
	"edit":         "△",
	"bash":         "$",
	"glob":         "◇",
	"grep":         "⊙",
	"todo":         "☐",
	"diff":         "±",
	"tree":         "⊞",
	"web_fetch":    "↗",
	"web_search":   "↗",
	"list_files":   "◇",
	"list_dir":     "◇",
	"file_search":  "⊙",
	"code_search":  "⊙",
	"ask_question": "?",
	"git_log":      "⎇",
	"git_diff":     "⎇",
	"git_blame":    "⎇",
	"git_add":      "⎇",
	"git_branch":   "⎇",
	"git_commit":   "⎇",
	"git_pr":       "⎇",
	"git_status":   "⎇",
	"commit":       "⎇",
	"copy":         "✦",
	"delete":       "×",
	"mkdir":        "✦",
	"move":         "△",
	"redo":         "↪",
	"memory":       "◈",
	"memorize":     "◈",
	"refactor":     "△",
	"batch":        "▪",
	"task":         "▪",
	"test":         "▪",
	"build":        "▪",
	"default":      "·",
}

// GetToolIcon returns the icon for a given tool name.
func GetToolIcon(toolName string) string {
	// Normalize tool name (lowercase, underscores)
	normalized := strings.ToLower(strings.ReplaceAll(toolName, "-", "_"))

	if icon, ok := ToolIcons[normalized]; ok {
		return icon
	}
	return ToolIcons["default"]
}

// GetToolIconColor returns the semantic color for a given tool name.
func GetToolIconColor(toolName string) lipgloss.Color {
	// Normalize tool name
	normalized := strings.ToLower(strings.ReplaceAll(toolName, "-", "_"))

	// Tool-specific semantic colors
	colors := map[string]lipgloss.Color{
		"read":        ColorPrimary,   // Purple - file operations
		"write":       ColorSuccess,   // Green - creation
		"edit":        ColorWarning,   // Amber - modification
		"bash":        ColorRunning,   // Blue - execution
		"glob":        ColorSecondary, // Cyan - search
		"grep":        ColorInfo,      // Teal - pattern search
		"todo":        ColorAccent,    // Pink - tasks
		"diff":        ColorPrimary,   // Purple - comparison
		"tree":        ColorSuccess,   // Green - structure
		"web_fetch":   ColorGradient2, // Indigo - network
		"web_search":  ColorGradient2, // Indigo - network
		"git_log":     ColorWarning,   // Amber - history
		"git_diff":    ColorPrimary,   // Purple - changes
		"git_blame":   ColorSecondary, // Cyan - attribution
		"git_add":     ColorSuccess,   // Green - staging
		"git_branch":  ColorWarning,   // Amber - branch operations
		"git_commit":  ColorSuccess,   // Green - save
		"git_status":  ColorInfo,      // Teal - repository state
		"git_pr":      ColorGradient2, // Indigo - remote collaboration
		"commit":      ColorSuccess,   // Green - save
		"copy":        ColorSuccess,   // Green - creation
		"delete":      ColorError,     // Red - removal
		"mkdir":       ColorSuccess,   // Green - creation
		"move":        ColorWarning,   // Amber - relocation
		"memory":      ColorGradient1, // Purple - storage
		"refactor":    ColorWarning,   // Amber - transformation
		"batch":       ColorAccent,    // Pink - bulk
		"run_tests":   ColorRunning,   // Blue - testing
		"verify_code": ColorInfo,      // Teal - verification
		"test":        ColorRunning,   // Blue - testing
		"build":       ColorWarning,   // Amber - compilation
	}

	if color, ok := colors[normalized]; ok {
		return color
	}
	return ColorMuted // Default gray
}

// Styles contains all UI styles.
type Styles struct {
	App           lipgloss.Style
	Header        lipgloss.Style
	UserPrompt    lipgloss.Style
	AssistantText lipgloss.Style
	ThoughtText   lipgloss.Style
	ToolCall      lipgloss.Style
	ToolResult    lipgloss.Style
	Error         lipgloss.Style
	Warning       lipgloss.Style
	Spinner       lipgloss.Style
	StatusBar     lipgloss.Style
	Input         lipgloss.Style
	Viewport      lipgloss.Style
	TodoItem      lipgloss.Style
	TodoPending   lipgloss.Style
	TodoActive    lipgloss.Style
	TodoDone      lipgloss.Style

	// Box styles for structured output
	InfoBox    lipgloss.Style
	WarningBox lipgloss.Style
	SuccessBox lipgloss.Style
	ErrorBox   lipgloss.Style

	// Additional styles
	Dim       lipgloss.Style
	Highlight lipgloss.Style
	Accent    lipgloss.Style

	// Modal styles (unified across question prompt, plan approval, model selector)
	ModalTitle    lipgloss.Style // Bold title with ColorQuestion/ColorPlan
	ModalSelected lipgloss.Style // Selected option (bold + ColorSecondary)
	ModalNormal   lipgloss.Style // Normal option (ColorMuted)
	ModalMuted    lipgloss.Style // Dimmed text (ColorDim + italic)
	ModalDefault  lipgloss.Style // Default indicator (ColorContext + italic)

	// Markdown styles
	InlineCode lipgloss.Style // Style for inline code blocks (backticks)

	// Code block styles
	CodeBlockBorder   lipgloss.Style // Border for code blocks
	CodeBlockHeader   lipgloss.Style // Header with filename/language
	CodeBlockSelected lipgloss.Style // Selected code block border
	CodeBlockActions  lipgloss.Style // Action hints in header

	// Tool execution block styles
	ToolBlock     lipgloss.Style // Container for entire tool execution
	ToolHeader    lipgloss.Style // Header with icon + name
	ToolContent   lipgloss.Style // Tool output content
	ToolSeparator lipgloss.Style // Separator line between tools
	ToolSpinner   lipgloss.Style // Animated spinner for active tools
	ToolTiming    lipgloss.Style // Timing information

	// Segmented Status Bar styles
	StatusSegment      lipgloss.Style
	StatusSegmentBold  lipgloss.Style
	StatusSeparator    lipgloss.Style
	StatusSectionName  lipgloss.Style
	StatusSectionValue lipgloss.Style

	// Card styles for message grouping
	UserCard      lipgloss.Style
	AssistantCard lipgloss.Style
}

// DefaultStyles returns the default UI styles.
func DefaultStyles() *Styles {
	return &Styles{
		App: lipgloss.NewStyle(),

		Header: lipgloss.NewStyle().
			Bold(true).
			Foreground(ColorPrimary).
			MarginBottom(1),

		UserPrompt: lipgloss.NewStyle().
			Foreground(ColorSecondary).
			Bold(true),

		AssistantText: lipgloss.NewStyle().
			Foreground(ColorText).
			Padding(0, 1),

		ThoughtText: lipgloss.NewStyle().
			Foreground(ColorDim).
			Italic(true),

		ToolCall: lipgloss.NewStyle().
			Foreground(ColorSecondary).
			Bold(true).
			Background(ColorSlate).
			Padding(0, 1),

		ToolResult: lipgloss.NewStyle().
			Foreground(ColorMuted).
			MarginLeft(2),

		Error: lipgloss.NewStyle().
			Foreground(ColorError).
			Bold(true),

		Warning: lipgloss.NewStyle().
			Foreground(ColorWarning).
			Bold(true),

		Spinner: lipgloss.NewStyle().
			Foreground(ColorPrimary),

		StatusBar: lipgloss.NewStyle().
			Foreground(ColorMuted).
			Background(ColorBg).
			Padding(0, 1),

		Input: lipgloss.NewStyle().
			BorderStyle(lipgloss.RoundedBorder()).
			BorderForeground(ColorPrimary).
			Padding(0, 1),

		Viewport: lipgloss.NewStyle().
			MarginBottom(1),

		TodoItem: lipgloss.NewStyle().
			MarginLeft(2),

		TodoPending: lipgloss.NewStyle().
			Foreground(ColorMuted),

		TodoActive: lipgloss.NewStyle().
			Foreground(ColorWarning).
			Bold(true),

		TodoDone: lipgloss.NewStyle().
			Foreground(ColorSuccess),

		// Box styles for structured output. ErrorBox has a rounded red
		// border so errors visually separate from streaming output — users
		// previously missed actionable suggestions that blended into text.
		InfoBox:    lipgloss.NewStyle().MarginTop(1),
		WarningBox: lipgloss.NewStyle().MarginTop(1),
		SuccessBox: lipgloss.NewStyle().MarginTop(1),
		ErrorBox: lipgloss.NewStyle().
			Border(lipgloss.RoundedBorder()).
			BorderForeground(ColorError).
			Padding(0, 1).
			MarginTop(1),

		// Additional styles
		Dim: lipgloss.NewStyle().
			Foreground(ColorDim),

		Highlight: lipgloss.NewStyle().
			Foreground(ColorHighlight),

		Accent: lipgloss.NewStyle().
			Foreground(ColorAccent),

		// Modal styles (unified across prompts)
		ModalTitle: lipgloss.NewStyle().
			Bold(true).
			Foreground(ColorSecondary).
			Padding(0, 1),

		ModalSelected: lipgloss.NewStyle().
			Bold(true).
			Foreground(ColorSecondary),

		ModalNormal: lipgloss.NewStyle().
			Foreground(ColorMuted),

		ModalMuted: lipgloss.NewStyle().
			Foreground(ColorDim).
			Italic(true),

		ModalDefault: lipgloss.NewStyle().
			Foreground(ColorContext).
			Italic(true),

		// Markdown styles
		//
		// InlineCode uses ColorBorder (not ColorSlate=surface) as the
		// background — surface (#16191F) is only ~8/256 brighter than
		// the chat background (#0E1116), too subtle to register as an
		// "inline code" affordance on bright monitors. Border (#2A2C33)
		// gives ~28/256 of contrast — readable in any lighting.
		InlineCode: lipgloss.NewStyle().
			Foreground(ColorLavender).
			Background(ColorBorder).
			Padding(0, 1),

		// Code block styles
		CodeBlockBorder: lipgloss.NewStyle().
			Foreground(ColorBorder),

		CodeBlockHeader: lipgloss.NewStyle().
			Bold(true).
			Foreground(ColorAccent),

		CodeBlockSelected: lipgloss.NewStyle().
			Foreground(ColorSecondary).
			Bold(true),

		CodeBlockActions: lipgloss.NewStyle().
			Foreground(ColorInfo),

		// Tool execution block styles
		ToolBlock: lipgloss.NewStyle().
			MarginTop(1).
			MarginBottom(1).
			Padding(0, 1),

		ToolHeader: lipgloss.NewStyle().
			Bold(true).
			Foreground(ColorPrimary).
			Background(ColorSlate).
			Padding(0, 1).
			MarginBottom(1),

		ToolContent: lipgloss.NewStyle().
			Foreground(ColorText).
			PaddingLeft(2),

		ToolSeparator: lipgloss.NewStyle().
			Foreground(ColorDim).
			MarginTop(1).
			MarginBottom(1),

		ToolSpinner: lipgloss.NewStyle().
			Foreground(ColorInfo).
			Bold(true),

		ToolTiming: lipgloss.NewStyle().
			Foreground(ColorMuted).
			Italic(true),

		// Segmented Status Bar (Powerline inspired)
		StatusSegment: lipgloss.NewStyle().
			Background(ColorBg).
			Padding(0, 1),

		StatusSegmentBold: lipgloss.NewStyle().
			Background(ColorBg).
			Bold(true).
			Padding(0, 1),

		StatusSeparator: lipgloss.NewStyle().
			Foreground(ColorBg).
			Background(ColorBorder),

		StatusSectionName: lipgloss.NewStyle().
			Foreground(ColorDim).
			Bold(true),

		StatusSectionValue: lipgloss.NewStyle().
			Foreground(ColorText),

		// Card styles
		UserCard: lipgloss.NewStyle().
			PaddingLeft(2).
			MarginTop(1),

		AssistantCard: lipgloss.NewStyle().
			PaddingLeft(2).
			MarginTop(1),
	}
}

func (s *Styles) FormatUserMessage(msg string) string {
	// Violet › prefix matches the input prompt — same glyph signals
	// "your turn" both in scrollback history and at the active input
	// row. Body in warm off-white for readable contrast.
	promptStyle := lipgloss.NewStyle().Foreground(ColorPrimary).Bold(true)
	contentStyle := lipgloss.NewStyle().Foreground(ColorText)

	lines := strings.Split(msg, "\n")
	var result strings.Builder
	for i, line := range lines {
		if i == 0 {
			result.WriteString(promptStyle.Render("› ") + contentStyle.Render(line))
		} else {
			result.WriteString("\n  " + contentStyle.Render(line))
		}
	}
	return s.UserCard.Render(result.String())
}

// FormatAssistantMessage formats an assistant message.
func (s *Styles) FormatAssistantMessage(msg string) string {
	return s.AssistantCard.Render(s.AssistantText.Render(msg))
}

// FormatAssistantStreaming formats streaming assistant text (without header).
// Used during streaming to avoid repeating the header.
func (s *Styles) FormatAssistantStreaming(msg string) string {
	return s.AssistantText.Render(msg)
}

// FormatToolCall formats a tool call notification.
func (s *Styles) FormatToolCall(name string) string {
	return s.ToolCall.Render(name)
}

// FormatToolCallWithArgs formats a tool call with brief argument summary.
func (s *Styles) FormatToolCallWithArgs(name string, args map[string]any) string {
	summary := formatArgsSummary(args)
	if summary != "" {
		return s.ToolCall.Render(name + " " + summary)
	}
	return s.ToolCall.Render(name)
}

// FormatToolExecuting formats a tool that is currently executing.
func (s *Styles) FormatToolExecuting(name string, args map[string]any) string {
	summary := formatArgsSummary(args)

	spinner := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
	idx := int(time.Now().Unix()/100) % len(spinner)

	exeStyle := lipgloss.NewStyle().Foreground(ColorDim)

	if summary != "" {
		return exeStyle.Render(spinner[idx] + " " + name + " " + summary)
	}
	return exeStyle.Render(spinner[idx] + " " + name)
}

// FormatToolExecutingBlock formats a tool execution as a compact line.
// Format: ▸ Read /path/to/file.go  (per-tool colored icon)
func (s *Styles) FormatToolExecutingBlock(name string, args map[string]any) string {
	// Per-tool colored icon (using existing ToolIcons + GetToolIconColor)
	icon := GetToolIcon(name)
	iconColor := GetToolIconColor(name)
	iconStyle := lipgloss.NewStyle().Foreground(iconColor)

	// Tool name — same color as icon but not bold
	nameStyle := lipgloss.NewStyle().Foreground(iconColor)

	// Args — soft white, not gray (more readable)
	argsStyle := lipgloss.NewStyle().Foreground(ColorText)

	var result strings.Builder
	result.WriteString(iconStyle.Render(icon + " "))
	result.WriteString(nameStyle.Render(capitalizeToolName(name)))

	argsStr := buildClaudeCodeArgs(name, args)
	if argsStr != "" {
		result.WriteString(" ")
		result.WriteString(argsStyle.Render(argsStr))
	}

	return result.String()
}

// FormatToolSuccess formats a successful tool result.
func (s *Styles) FormatToolSuccess(name string, duration time.Duration) string {
	successStyle := lipgloss.NewStyle().Foreground(ColorSuccess)
	dimStyle := lipgloss.NewStyle().Foreground(ColorDim)

	durationStr := ""
	if duration < time.Second {
		durationStr = fmt.Sprintf("%dms", duration.Milliseconds())
	} else if duration < time.Minute {
		durationStr = fmt.Sprintf("%.1fs", duration.Seconds())
	}

	if durationStr != "" {
		return successStyle.Render("✓ "+name) + "  " + dimStyle.Render(durationStr)
	}
	return successStyle.Render("✓ " + name)
}

// minDisplayedToolDuration is the floor below which a tool's timing is omitted
// from its success line. Sub-100ms operations are effectively instant; a
// trailing "9ms" on every read/grep is noise during exploration. Slow tools
// (≥100ms) still show timing — that's the part worth noticing.
const minDisplayedToolDuration = 100 * time.Millisecond

// FormatToolSuccessBlock formats a successful tool result as a compact line.
// Format: ✓ name  summary  duration
func (s *Styles) FormatToolSuccessBlock(name string, duration time.Duration, resultSummary string) string {
	checkStyle := lipgloss.NewStyle().Foreground(ColorSuccess)
	nameStyle := lipgloss.NewStyle().Foreground(ColorMuted)
	summaryStyle := lipgloss.NewStyle().Foreground(ColorDim)

	var result strings.Builder

	result.WriteString(checkStyle.Render("✓ "))
	result.WriteString(nameStyle.Render(capitalizeToolName(name)))

	if resultSummary != "" {
		result.WriteString("  ")
		result.WriteString(summaryStyle.Render(resultSummary))
	}

	durationStr := ""
	durationColor := ColorDim
	if duration < time.Second {
		durationStr = fmt.Sprintf("%dms", duration.Milliseconds())
	} else if duration < time.Minute {
		secs := duration.Seconds()
		durationStr = fmt.Sprintf("%.1fs", secs)
		if secs > 5 {
			durationColor = ColorWarning
		}
	} else {
		durationStr = fmt.Sprintf("%.1fm", duration.Minutes())
		durationColor = ColorWarning
	}

	if durationStr != "" && duration >= minDisplayedToolDuration {
		timingStyle := lipgloss.NewStyle().Foreground(durationColor)
		result.WriteString("  ")
		result.WriteString(timingStyle.Render(durationStr))
	}

	return result.String()
}

// FormatToolError formats a failed tool result.
func (s *Styles) FormatToolError(name string, err error) string {
	errorStyle := lipgloss.NewStyle().Foreground(ColorRose)
	msgStyle := lipgloss.NewStyle().Foreground(ColorDim)

	return errorStyle.Render("✗ "+name) + "  " + msgStyle.Render(err.Error())
}

// --- Agent Activity Formatting ---
// Agent tool calls shown with dim type prefix:
//   explore ▸ Read internal/app/app.go
//   explore ▸ Grep "pattern" ./internal/
//   explore ✓ 5.2s
// Flat format because background agents are async — their events
// interleave with other output, making tree connectors unreliable.

// FormatAgentToolCall renders an agent's tool call with type prefix.
func (s *Styles) FormatAgentToolCall(agentType string, toolName string, args map[string]any) string {
	prefixStyle := lipgloss.NewStyle().Foreground(ColorMuted)
	return prefixStyle.Render("  "+agentType+" ") + s.FormatToolExecutingBlock(toolName, args)
}

// FormatAgentComplete renders agent completion.
func (s *Styles) FormatAgentComplete(agentType string, elapsed time.Duration) string {
	prefixStyle := lipgloss.NewStyle().Foreground(ColorMuted)
	checkStyle := lipgloss.NewStyle().Foreground(ColorSuccess)
	durStyle := lipgloss.NewStyle().Foreground(ColorDim)

	dur := formatCompactDuration(elapsed)
	return prefixStyle.Render("  "+agentType+" ") + checkStyle.Render("✓") + " " + durStyle.Render(dur)
}

// FormatAgentFailed renders agent failure.
func (s *Styles) FormatAgentFailed(agentType string, elapsed time.Duration) string {
	prefixStyle := lipgloss.NewStyle().Foreground(ColorMuted)
	crossStyle := lipgloss.NewStyle().Foreground(ColorError)
	durStyle := lipgloss.NewStyle().Foreground(ColorDim)

	dur := formatCompactDuration(elapsed)
	return prefixStyle.Render("  "+agentType+" ") + crossStyle.Render("✗") + " " + durStyle.Render(dur)
}

// formatArgsSummary creates a brief summary of tool arguments.
func formatArgsSummary(args map[string]any) string {
	if len(args) == 0 {
		return ""
	}

	// Priority keys to show first
	priorityKeys := []string{"path", "file_path", "command", "pattern", "content", "query"}

	var parts []string
	shown := make(map[string]bool)

	// Show priority keys first
	for _, key := range priorityKeys {
		if val, ok := args[key]; ok {
			str := formatArgValue(val)
			if str != "" {
				parts = append(parts, key+"="+str)
				shown[key] = true
				if len(parts) >= 2 {
					break
				}
			}
		}
	}

	// Add other keys if we have room
	for key, val := range args {
		if shown[key] {
			continue
		}
		if len(parts) >= 2 {
			break
		}
		str := formatArgValue(val)
		if str != "" {
			parts = append(parts, key+"="+str)
		}
	}

	if len(parts) == 0 {
		return ""
	}

	result := "(" + lipgloss.NewStyle().Foreground(ColorMuted).Render(
		joinStrings(parts, ", "),
	) + ")"
	return result
}

// formatArgValue formats a single argument value for display.
func formatArgValue(val any) string {
	switch v := val.(type) {
	case string:
		// Don't truncate here - let the caller decide based on context
		// This allows file paths to be shown in full
		return "\"" + v + "\""
	case float64:
		return fmt.Sprintf("%.0f", v)
	case bool:
		if v {
			return "true"
		}
		return "false"
	default:
		return ""
	}
}

// joinStrings joins strings with a separator.
func joinStrings(parts []string, sep string) string {
	return strings.Join(parts, sep)
}

// FormatError formats an error message with proper styling.
func (s *Styles) FormatError(err string) string {
	return s.FormatErrorWithSuggestion(err, "", "")
}

// FormatErrorWithSuggestion formats an error message with a suggestion - Claude Code style.
func (s *Styles) FormatErrorWithSuggestion(err, suggestion, code string) string {
	errorStyle := lipgloss.NewStyle().Foreground(ColorError).Bold(true)
	msgStyle := lipgloss.NewStyle().Foreground(ColorText)
	suggestionStyle := lipgloss.NewStyle().Foreground(ColorWarning)
	codeStyle := lipgloss.NewStyle().Foreground(ColorDim)
	markerStyle := lipgloss.NewStyle().Foreground(ColorDim)

	var result strings.Builder
	result.WriteString(errorStyle.Render("✗ Error: ") + msgStyle.Render(err))

	if suggestion != "" {
		result.WriteString("\n" + markerStyle.Render("  ⎿  ") + suggestionStyle.Render(suggestion))
	}
	if code != "" {
		result.WriteString("\n" + markerStyle.Render("     ") + codeStyle.Render("("+code+")"))
	}
	return result.String()
}

// capitalizeToolName converts snake_case tool names to PascalCase.
// e.g., "web_fetch" → "WebFetch", "bash" → "Bash", "read" → "Read"
func capitalizeToolName(name string) string {
	if name == "" {
		return ""
	}
	parts := strings.Split(name, "_")
	for i, p := range parts {
		if len(p) > 0 {
			parts[i] = strings.ToUpper(p[:1]) + p[1:]
		}
	}
	return strings.Join(parts, "")
}

// buildClaudeCodeArgs formats tool arguments for display in Claude Code style.
// Returns a string like "file_path=/path" or "command" for bash.
func buildClaudeCodeArgs(name string, args map[string]any) string {
	if len(args) == 0 {
		return ""
	}

	var result string
	isFilePath := false

	switch name {
	case "bash":
		if cmd, ok := args["command"]; ok {
			result = formatArgValue(cmd)
		}
	case "read", "write", "edit":
		if path, ok := args["file_path"]; ok {
			result = formatArgValue(path)
			isFilePath = true
		}
	case "copy", "move":
		result = formatPathPair(toolStringArg(args, "source"), toolStringArg(args, "destination"), 70)
	case "delete", "mkdir":
		if path := toolStringArg(args, "path"); path != "" {
			result = path
			isFilePath = true
		}
	case "run_tests":
		result = formatRunTestsTarget(args, 45)
	case "verify_code":
		path := toolStringArg(args, "path")
		if path == "" {
			path = "."
		}
		result = shortenPath(path, 55)
	case "git_status":
		path := toolStringArg(args, "path")
		if path != "" {
			result = shortenPath(path, 45)
		}
		if toolBoolArg(args, "short") {
			if result != "" {
				result += " "
			}
			result += "--short"
		}
	case "git_diff":
		result = formatGitDiffTarget(args, 55)
	case "git_add":
		result = formatGitAddTarget(args, 55)
	case "git_branch":
		result = strings.TrimSpace(toolStringArg(args, "action") + " " + toolStringArg(args, "name"))
	case "git_log":
		if file := toolStringArg(args, "file"); file != "" {
			result = shortenPath(file, 45)
		} else if grep := toolStringArg(args, "grep"); grep != "" {
			result = "grep=" + compactInline(grep, 36)
		} else if author := toolStringArg(args, "author"); author != "" {
			result = "author=" + compactInline(author, 36)
		} else if count := toolIntArg(args, "count"); count > 0 {
			result = fmt.Sprintf("%d commits", count)
		}
	case "grep":
		if pattern, ok := args["pattern"]; ok {
			result = "pattern=" + formatArgValue(pattern)
		}
		if path, ok := args["path"]; ok {
			result += " path=" + formatArgValue(path)
		}
	case "glob":
		if pattern, ok := args["pattern"]; ok {
			result = "pattern=" + formatArgValue(pattern)
		}
	case "web_fetch":
		if url, ok := args["url"]; ok {
			result = "url=" + formatArgValue(url)
		}
	case "web_search":
		if query, ok := args["query"]; ok {
			result = "query=" + formatArgValue(query)
		}
	default:
		// Use first priority arg
		for _, key := range []string{"file_path", "path", "directory_path", "source", "destination", "command", "pattern", "query", "url", "action", "operation", "name"} {
			if val, ok := args[key]; ok {
				result = key + "=" + formatArgValue(val)
				if key == "file_path" || key == "path" || key == "directory_path" || key == "source" || key == "destination" {
					isFilePath = true
				}
				break
			}
		}
	}

	// Smart truncation. File paths get an aggressive budget + ~/ prefix
	// for home directory + middle-ellipsis (shortenPath), because the
	// success line right below this one repeats the path — long absolute
	// paths like /Users/alice/github/project/internal/ui/tui.go produced
	// two ugly duplicated rows. 55 cols keeps the filename visible even
	// in narrow terminals while fitting the exec header on one line.
	const filePathMaxLen = 55
	const generalArgMaxLen = 120
	if isFilePath {
		if stripped := stripWrappingQuotes(result); stripped != result {
			// shortenPath expects a bare path, not a quoted one. Re-wrap.
			result = "\"" + shortenPath(stripped, filePathMaxLen) + "\""
		} else {
			result = shortenPath(result, filePathMaxLen)
		}
	} else if runes := []rune(result); len(runes) > generalArgMaxLen {
		result = string(runes[:generalArgMaxLen-3]) + "..."
	}

	return result
}

// stripWrappingQuotes returns s without its single wrapping pair of double
// quotes, or s unchanged. Used to unwrap formatArgValue's quoted strings
// before path-aware truncation; callers re-wrap.
func stripWrappingQuotes(s string) string {
	if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
		return s[1 : len(s)-1]
	}
	return s
}

// RenderToolSeparator renders a visual separator between tool executions.
func (s *Styles) RenderToolSeparator() string {
	separator := strings.Repeat("─", 80)
	return s.ToolSeparator.Render(separator)
}

// RenderMessageSeparator renders a subtle separator between messages.
func (s *Styles) RenderMessageSeparator() string {
	separatorStyle := lipgloss.NewStyle().
		Foreground(ColorDim)
	return separatorStyle.Render(strings.Repeat("─", 60))
}

// FormatThinkingIndicator formats the thinking/processing indicator with braille spinner.
func (s *Styles) FormatThinkingIndicator() string {
	spinners := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
	idx := int(time.Now().UnixMilli()/100) % len(spinners)

	spinnerStyle := lipgloss.NewStyle().Foreground(ColorDim)
	textStyle := lipgloss.NewStyle().Foreground(ColorDim)

	return spinnerStyle.Render(spinners[idx]) + textStyle.Render(" Thinking")
}

// FormatThinkingIndicatorWithTokens formats the thinking indicator with token usage.
func (s *Styles) FormatThinkingIndicatorWithTokens(usedTokens, maxTokens int) string {
	spinners := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
	idx := int(time.Now().UnixMilli()/100) % len(spinners)

	spinnerStyle := lipgloss.NewStyle().Foreground(ColorDim)
	textStyle := lipgloss.NewStyle().Foreground(ColorDim)
	dimStyle := lipgloss.NewStyle().Foreground(ColorDim)

	// Format token count
	var tokenStr string
	if usedTokens >= 1000 {
		tokenStr = fmt.Sprintf("%.1fk", float64(usedTokens)/1000)
	} else {
		tokenStr = fmt.Sprintf("%d", usedTokens)
	}
	var maxStr string
	if maxTokens >= 1000 {
		maxStr = fmt.Sprintf("%.0fk", float64(maxTokens)/1000)
	} else {
		maxStr = fmt.Sprintf("%d", maxTokens)
	}

	return spinnerStyle.Render(spinners[idx]) + textStyle.Render(" Thinking") +
		dimStyle.Render("  ["+tokenStr+"/"+maxStr+"]")
}

// FormatPlanStepHeader formats a plan step header for delegated execution output.
func (s *Styles) FormatPlanStepHeader(stepID, totalSteps int, title string) string {
	headerStyle := lipgloss.NewStyle().
		Bold(true).
		Foreground(ColorPrimary)
	progressStyle := lipgloss.NewStyle().
		Foreground(ColorAccent)
	borderStyle := lipgloss.NewStyle().
		Foreground(ColorDim)

	progress := progressStyle.Render(fmt.Sprintf("[%d/%d]", stepID, totalSteps))
	header := headerStyle.Render(title)
	line := borderStyle.Render(strings.Repeat("─", 60))

	return line + "\n" + progress + " " + header + "\n"
}

// FormatPlanStepResult formats a plan step completion result.
func (s *Styles) FormatPlanStepResult(stepID int, success bool, summary string) string {
	if success {
		checkStyle := lipgloss.NewStyle().Foreground(ColorSuccess).Bold(true)
		summaryStyle := lipgloss.NewStyle().Foreground(ColorText)
		result := checkStyle.Render(fmt.Sprintf("  Step %d done", stepID))
		if summary != "" {
			// Truncate summary for display
			if runes := []rune(summary); len(runes) > 120 {
				summary = string(runes[:117]) + "..."
			}
			result += " " + summaryStyle.Render(summary)
		}
		return result
	}

	failStyle := lipgloss.NewStyle().Foreground(ColorError).Bold(true)
	msgStyle := lipgloss.NewStyle().Foreground(ColorText)
	result := failStyle.Render(fmt.Sprintf("  Step %d failed", stepID))
	if summary != "" {
		result += " " + msgStyle.Render(summary)
	}
	return result
}

// FormatPlanBanner formats the plan execution start/end banner.
func (s *Styles) FormatPlanBanner(text string, isStart bool) string {
	color := ColorSuccess
	if isStart {
		color = ColorInfo
	}
	borderStyle := lipgloss.NewStyle().Foreground(color)
	textStyle := lipgloss.NewStyle().Foreground(color).Bold(true)

	line := strings.Repeat("─", 60)
	return borderStyle.Render(line) + "\n" + textStyle.Render(text) + "\n" + borderStyle.Render(line)
}

// FormatMessage formats a message with consistent styling based on type.
// msgType can be: success, error, warning, info, hint, loading
func (s *Styles) FormatMessage(msgType, title, body string) string {
	icon := MessageIcons[msgType]
	if icon == "" {
		icon = MessageIcons["info"]
	}

	var color lipgloss.Color

	switch msgType {
	case "success", "done":
		color = ColorSuccess
	case "error":
		color = ColorError
	case "warning":
		color = ColorWarning
	case "hint":
		color = ColorAccent
	default: // info, loading
		color = ColorInfo
	}

	headerStyle := lipgloss.NewStyle().
		Bold(true).
		Foreground(color)

	bodyStyle := lipgloss.NewStyle().
		Foreground(ColorText).
		PaddingLeft(2)

	header := headerStyle.Render(icon + " " + title)
	if body == "" {
		return header
	}

	return header + "\n" + bodyStyle.Render(body)
}

// FormatHint formats a contextual hint message.
func (s *Styles) FormatHint(text string) string {
	hintStyle := lipgloss.NewStyle().
		Foreground(ColorAccent).
		Italic(true)
	iconStyle := lipgloss.NewStyle().
		Foreground(ColorAccent)

	return iconStyle.Render("› ") + hintStyle.Render(text)
}
