package ui

import (
	"fmt"
	"strings"
	"time"

	"github.com/charmbracelet/lipgloss"
)

// PlanStepStatus represents the status of a plan step.
type PlanStepStatus int

const (
	PlanStepPending PlanStepStatus = iota
	PlanStepInProgress
	PlanStepCompleted
	PlanStepFailed
	PlanStepSkipped
	PlanStepPaused
)

// PlanStepState holds the state of a single plan step.
type PlanStepState struct {
	ID            int
	Title         string
	Description   string
	Status        PlanStepStatus
	StartedAt     time.Time
	CompletedAt   time.Time
	Output        string
	Error         string
	FilesModified int
}

// StepTransition tracks a single state transition in the plan timeline.
type StepTransition struct {
	StepID  int
	From    PlanStepStatus
	To      PlanStepStatus
	Reason  string
	At      time.Time
	StepRef string
}

// ActivityEntry represents a single activity log entry.
type ActivityEntry struct {
	Timestamp time.Time
	Type      string // "tool", "file", "info"
	Message   string
}

// PlanProgressPanel displays detailed plan execution progress.
type PlanProgressPanel struct {
	visible       bool
	planID        string
	planTitle     string
	planDesc      string
	steps         []PlanStepState
	currentStepID int
	startedAt     time.Time
	styles        *Styles
	collapsed     bool // Compact mode
	frame         int  // For animations

	// Live activity feed
	activities    []ActivityEntry
	maxActivities int    // Max entries to keep (default: 5)
	currentTool   string // Currently executing tool
	currentInfo   string // Tool info (file path, command, etc.)
	timeline      []StepTransition
}

// NewPlanProgressPanel creates a new plan progress panel.
func NewPlanProgressPanel(styles *Styles) *PlanProgressPanel {
	return &PlanProgressPanel{
		visible:       false,
		steps:         make([]PlanStepState, 0),
		styles:        styles,
		collapsed:     false,
		frame:         0,
		activities:    make([]ActivityEntry, 0),
		maxActivities: 5,
		timeline:      make([]StepTransition, 0, 32),
	}
}

// planAutoCollapseSteps is the largest plan that still renders fully
// expanded by default. Above this the panel starts collapsed (current step +
// one summary row) — a 10-step plan otherwise pins a ~17-line box to the
// screen for the whole execution, which was the #1 "panel eats my screen"
// complaint. Ctrl+X expands on demand.
const planAutoCollapseSteps = 4

// StartPlan initializes a new plan execution.
func (p *PlanProgressPanel) StartPlan(planID, title, description string, steps []PlanStepInfo) {
	p.visible = true
	p.planID = planID
	p.planTitle = title
	p.planDesc = description
	p.startedAt = time.Now()
	p.currentStepID = 0
	p.collapsed = len(steps) > planAutoCollapseSteps
	p.timeline = p.timeline[:0]

	p.steps = make([]PlanStepState, len(steps))
	for i, step := range steps {
		p.steps[i] = PlanStepState{
			ID:          step.ID,
			Title:       step.Title,
			Description: step.Description,
			Status:      PlanStepPending,
		}
		p.appendTransition(step.ID, PlanStepPending, PlanStepPending, "queued")
	}
}

// StartStep marks a step as in progress.
func (p *PlanProgressPanel) StartStep(stepID int) {
	for i := range p.steps {
		if p.steps[i].ID == stepID {
			prev := p.steps[i].Status
			p.steps[i].Status = PlanStepInProgress
			p.steps[i].StartedAt = time.Now()
			p.currentStepID = stepID
			p.appendTransition(stepID, prev, PlanStepInProgress, "")
			break
		}
	}
}

// CompleteStep marks a step as completed.
func (p *PlanProgressPanel) CompleteStep(stepID int, output string, reason string) {
	for i := range p.steps {
		if p.steps[i].ID == stepID {
			prev := p.steps[i].Status
			p.steps[i].Status = PlanStepCompleted
			p.steps[i].CompletedAt = time.Now()
			p.steps[i].Output = output
			p.appendTransition(stepID, prev, PlanStepCompleted, reason)
			break
		}
	}
}

// FailStep marks a step as failed.
func (p *PlanProgressPanel) FailStep(stepID int, errorMsg string, reason string) {
	for i := range p.steps {
		if p.steps[i].ID == stepID {
			prev := p.steps[i].Status
			p.steps[i].Status = PlanStepFailed
			p.steps[i].CompletedAt = time.Now()
			p.steps[i].Error = errorMsg
			p.appendTransition(stepID, prev, PlanStepFailed, reason)
			break
		}
	}
}

// SkipStep marks a step as skipped.
func (p *PlanProgressPanel) SkipStep(stepID int, reason string) {
	for i := range p.steps {
		if p.steps[i].ID == stepID {
			prev := p.steps[i].Status
			p.steps[i].Status = PlanStepSkipped
			p.steps[i].CompletedAt = time.Now()
			p.appendTransition(stepID, prev, PlanStepSkipped, reason)
			break
		}
	}
}

// PauseStep marks a step as paused.
func (p *PlanProgressPanel) PauseStep(stepID int, reason string) {
	for i := range p.steps {
		if p.steps[i].ID == stepID {
			prev := p.steps[i].Status
			p.steps[i].Status = PlanStepPaused
			p.steps[i].CompletedAt = time.Now()
			p.steps[i].Error = reason
			p.appendTransition(stepID, prev, PlanStepPaused, reason)
			break
		}
	}
}

func (p *PlanProgressPanel) appendTransition(stepID int, from, to PlanStepStatus, reason string) {
	if from == to && reason == "" {
		return
	}
	stepRef := fmt.Sprintf("step %d", stepID)
	for _, s := range p.steps {
		if s.ID == stepID && s.Title != "" {
			stepRef = fmt.Sprintf("step %d (%s)", stepID, s.Title)
			break
		}
	}
	p.timeline = append(p.timeline, StepTransition{
		StepID:  stepID,
		From:    from,
		To:      to,
		Reason:  reason,
		At:      time.Now(),
		StepRef: stepRef,
	})
	if len(p.timeline) > 40 {
		p.timeline = p.timeline[len(p.timeline)-40:]
	}
}

// EndPlan marks the plan as finished.
func (p *PlanProgressPanel) EndPlan() {
	// Don't hide immediately - let user see final state
	// Panel will be hidden when a new response starts
}

// Hide hides the panel.
func (p *PlanProgressPanel) Hide() {
	p.visible = false
}

// IsVisible returns whether the panel is visible.
func (p *PlanProgressPanel) IsVisible() bool {
	return p.visible
}

// Toggle toggles collapsed mode.
func (p *PlanProgressPanel) Toggle() {
	p.collapsed = !p.collapsed
}

// Tick updates the animation frame.
func (p *PlanProgressPanel) Tick() {
	p.frame++
}

// SetCurrentTool updates the currently executing tool.
func (p *PlanProgressPanel) SetCurrentTool(toolName, toolInfo string) {
	if toolName == "" {
		p.currentTool = ""
		p.currentInfo = ""
		return
	}

	p.currentTool = toolName
	p.currentInfo = toolInfo

	// Track file modifications for current step
	switch toolName {
	case "write", "edit", "delete", "copy", "move":
		for i := range p.steps {
			if p.steps[i].ID == p.currentStepID {
				p.steps[i].FilesModified++
				break
			}
		}
	}

	// Add to activity log
	msg := toolName
	if toolInfo != "" {
		if runes := []rune(toolInfo); len(runes) > 40 {
			toolInfo = string(runes[:37]) + "..."
		}
		msg += ": " + toolInfo
	}
	p.AddActivity("tool", msg)
}

// ClearCurrentTool clears the current tool.
func (p *PlanProgressPanel) ClearCurrentTool() {
	p.currentTool = ""
	p.currentInfo = ""
}

// AddActivity adds an entry to the activity log.
func (p *PlanProgressPanel) AddActivity(actType, message string) {
	entry := ActivityEntry{
		Timestamp: time.Now(),
		Type:      actType,
		Message:   message,
	}
	p.activities = append(p.activities, entry)

	// Trim to max size
	if len(p.activities) > p.maxActivities {
		p.activities = p.activities[len(p.activities)-p.maxActivities:]
	}
}

// ClearActivities clears the activity log.
func (p *PlanProgressPanel) ClearActivities() {
	p.activities = make([]ActivityEntry, 0)
}

// Progress returns the completion progress (0.0 to 1.0).
func (p *PlanProgressPanel) Progress() float64 {
	if len(p.steps) == 0 {
		return 0
	}

	completed := 0
	for _, step := range p.steps {
		if step.Status == PlanStepCompleted || step.Status == PlanStepSkipped {
			completed++
		}
	}
	return float64(completed) / float64(len(p.steps))
}

// CompletedCount returns the number of completed steps.
func (p *PlanProgressPanel) CompletedCount() int {
	count := 0
	for _, step := range p.steps {
		if step.Status == PlanStepCompleted || step.Status == PlanStepSkipped {
			count++
		}
	}
	return count
}

// View renders the plan progress panel.
func (p *PlanProgressPanel) View(width int) string {
	if !p.visible || len(p.steps) == 0 {
		return ""
	}

	// Styles
	borderStyle := lipgloss.NewStyle().Foreground(ColorPlan)
	titleStyle := lipgloss.NewStyle().Bold(true).Foreground(ColorPlan)
	dimStyle := lipgloss.NewStyle().Foreground(ColorDim)
	mutedStyle := lipgloss.NewStyle().Foreground(ColorMuted)

	var content strings.Builder

	// Panel width
	panelWidth := width - 4
	if panelWidth < 50 {
		panelWidth = 50
	}
	if panelWidth > 100 {
		panelWidth = 100
	}

	// Header with progress bar
	progress := p.Progress()
	elapsed := time.Since(p.startedAt)

	// Progress bar
	barWidth := 20
	filled := int(progress * float64(barWidth))
	if filled > barWidth {
		filled = barWidth
	}
	progressBar := p.renderProgressBar(filled, barWidth, progress)

	// Header line
	header := fmt.Sprintf(" Plan: %s ", p.planTitle)
	if runes := []rune(header); len(runes) > panelWidth-30 {
		header = string(runes[:panelWidth-33]) + "... "
	}

	progressInfo := fmt.Sprintf(" %d/%d ", p.CompletedCount(), len(p.steps))
	elapsedStr := formatElapsed(elapsed)

	headerLine := titleStyle.Render(header) +
		progressBar + " " +
		mutedStyle.Render(progressInfo) +
		dimStyle.Render("("+elapsedStr+")")

	// Premium 3D-like border using distinct characters
	content.WriteString(borderStyle.Render("╭") + borderStyle.Render(strings.Repeat("─", panelWidth-1)) + borderStyle.Render("╮"))
	content.WriteString("\n")

	// Title line stylized as a title bar
	titleBar := "  " + headerLine
	content.WriteString(borderStyle.Render("│"))
	content.WriteString(titleBar)
	padding := panelWidth - 1 - lipgloss.Width(titleBar)
	if padding > 0 {
		content.WriteString(strings.Repeat(" ", padding))
	}
	content.WriteString(borderStyle.Render("│"))
	content.WriteString("\n")

	content.WriteString(borderStyle.Render("├") + borderStyle.Render(strings.Repeat("─", panelWidth-1)) + borderStyle.Render("┤"))
	content.WriteString("\n")

	// Steps
	if !p.collapsed {
		for _, step := range p.steps {
			stepRendered := p.renderStep(step, panelWidth-4)
			p.writeBoxLines(&content, borderStyle, stepRendered, panelWidth)
		}
	} else {
		// Collapsed: show only current step
		shown := 0
		for _, step := range p.steps {
			if step.Status == PlanStepInProgress {
				stepRendered := p.renderStep(step, panelWidth-4)
				p.writeBoxLines(&content, borderStyle, stepRendered, panelWidth)
				shown = 1
				break
			}
		}

		// Compact summary with an HONEST hidden-step count and the REAL
		// expand key (the old text said "press Tab" — no Tab binding ever
		// existed; Ctrl+X is wired in tui.go handleGlobalKeys).
		hidden := len(p.steps) - shown
		summaryLine := dimStyle.Render(fmt.Sprintf("  … %d more · ✓ %d done (Ctrl+X to expand)", hidden, p.CompletedCount()))
		content.WriteString(borderStyle.Render("│"))
		content.WriteString(summaryLine)
		padding := panelWidth - 1 - lipgloss.Width(summaryLine)
		if padding > 0 {
			content.WriteString(strings.Repeat(" ", padding))
		}
		content.WriteString(borderStyle.Render("│"))
		content.WriteString("\n")
	}

	// Current tool activity (live indicator)
	if p.currentTool != "" {
		content.WriteString(borderStyle.Render("├") + borderStyle.Render(strings.Repeat("─", panelWidth-1)) + borderStyle.Render("┤"))
		content.WriteString("\n")

		// Animated spinner
		spinners := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
		spinner := spinners[p.frame%len(spinners)]

		toolStyle := lipgloss.NewStyle().Foreground(ColorWarning).Bold(true)
		infoStyle := lipgloss.NewStyle().Foreground(ColorAccent)

		toolLine := "  " + toolStyle.Render(spinner+" "+p.currentTool)
		if p.currentInfo != "" {
			info := p.currentInfo
			maxInfoLen := panelWidth - lipgloss.Width(toolLine) - 6
			if maxInfoLen > 0 {
				// truncateForWidth handles maxInfoLen in {1,2,3} without the
				// maxInfoLen-3 underflow panic the manual slice had.
				info = truncateForWidth(info, maxInfoLen)
			}
			if maxInfoLen > 0 {
				toolLine += " " + infoStyle.Render(info)
			}
		}

		content.WriteString(borderStyle.Render("│"))
		content.WriteString(toolLine)
		padding := panelWidth - 1 - lipgloss.Width(toolLine)
		if padding > 0 {
			content.WriteString(strings.Repeat(" ", padding))
		}
		content.WriteString(borderStyle.Render("│"))
		content.WriteString("\n")
	}

	// Footer
	content.WriteString(borderStyle.Render("╰" + strings.Repeat("─", panelWidth-1) + "╯"))

	return content.String()
}

// writeBoxLines writes a possibly multi-line rendered string inside the bordered box.
func (p *PlanProgressPanel) writeBoxLines(content *strings.Builder, borderStyle lipgloss.Style, rendered string, panelWidth int) {
	lines := strings.Split(rendered, "\n")
	for _, line := range lines {
		content.WriteString(borderStyle.Render("│ "))
		content.WriteString(line)
		padding := panelWidth - 3 - lipgloss.Width(line)
		if padding > 0 {
			content.WriteString(strings.Repeat(" ", padding))
		}
		content.WriteString(borderStyle.Render(" │"))
		content.WriteString("\n")
	}
}

// renderStep renders a single step with tree-style status icon.
// Uses animated spinner for in-progress, "✓" completed, "○" pending, "✗" failed, "⊘" skipped.
func (p *PlanProgressPanel) renderStep(step PlanStepState, maxWidth int) string {
	var icon string
	var iconStyle lipgloss.Style
	dimStyle := lipgloss.NewStyle().Foreground(ColorDim)

	switch step.Status {
	case PlanStepPending:
		icon = "○"
		iconStyle = lipgloss.NewStyle().Foreground(ColorDim)
	case PlanStepInProgress:
		// Animated braille spinner so it's obvious the step is actively running
		spinners := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
		icon = spinners[p.frame%len(spinners)]
		iconStyle = lipgloss.NewStyle().Foreground(ColorWarning).Bold(true)
	case PlanStepPaused:
		icon = "⏸"
		iconStyle = lipgloss.NewStyle().Foreground(ColorWarning).Bold(true)
	case PlanStepCompleted:
		icon = "✓"
		iconStyle = lipgloss.NewStyle().Foreground(ColorSuccess)
	case PlanStepFailed:
		icon = "✗"
		iconStyle = lipgloss.NewStyle().Foreground(ColorError)
	case PlanStepSkipped:
		icon = "⊘"
		iconStyle = lipgloss.NewStyle().Foreground(ColorMuted)
	}

	titleStyle := lipgloss.NewStyle().Foreground(ColorText)
	if step.Status == PlanStepInProgress {
		titleStyle = titleStyle.Bold(true)
	} else if step.Status == PlanStepPending {
		titleStyle = lipgloss.NewStyle().Foreground(ColorDim)
	}

	title := fmt.Sprintf("Step %d: %s", step.ID, step.Title)
	maxTitleWidth := maxWidth - 5 // icon + space + padding

	durationStr := ""
	switch step.Status {
	case PlanStepCompleted, PlanStepFailed:
		if !step.CompletedAt.IsZero() && !step.StartedAt.IsZero() {
			duration := step.CompletedAt.Sub(step.StartedAt)
			if step.FilesModified > 0 {
				durationStr = " " + dimStyle.Render(fmt.Sprintf("(%s · %d files)", formatElapsed(duration), step.FilesModified))
			} else {
				durationStr = " " + dimStyle.Render("("+formatElapsed(duration)+")")
			}
			maxTitleWidth -= lipgloss.Width(durationStr)
		}
	case PlanStepInProgress:
		// Show live elapsed time so users know how long the current step has been running
		if !step.StartedAt.IsZero() {
			durationStr = " " + dimStyle.Render("["+formatElapsed(time.Since(step.StartedAt))+"]")
			maxTitleWidth -= lipgloss.Width(durationStr)
		}
	}

	if maxTitleWidth < 10 {
		maxTitleWidth = 10
	}
	if runes := []rune(title); len(runes) > maxTitleWidth {
		title = string(runes[:maxTitleWidth-3]) + "..."
	}

	result := iconStyle.Render(icon) + " " + titleStyle.Render(title) + durationStr

	// For the active step: prioritise the currently-executing tool over the
	// static description — it's more actionable ("what is the agent doing RIGHT NOW").
	if step.Status == PlanStepInProgress {
		if p.currentStepID == step.ID && p.currentTool != "" {
			toolLine := p.currentTool
			if p.currentInfo != "" {
				info := p.currentInfo
				maxInfo := maxWidth - len(toolLine) - 9 // "    └─ " + ": " prefix
				if maxInfo < 10 {
					maxInfo = 10
				}
				if runes := []rune(info); len(runes) > maxInfo {
					info = string(runes[:maxInfo-3]) + "..."
				}
				toolLine += ": " + info
			}
			result += "\n    " + dimStyle.Render("└─ "+toolLine)
		} else if step.Description != "" {
			desc := step.Description
			if runes := []rune(desc); len(runes) > maxWidth-6 {
				desc = string(runes[:maxWidth-9]) + "..."
			}
			result += "\n    " + dimStyle.Italic(true).Render(desc)
		}
	}

	// Show first line of output for completed steps
	if step.Status == PlanStepCompleted && step.Output != "" {
		summary := firstLine(step.Output)
		if runes := []rune(summary); len(runes) > maxWidth-6 {
			summary = string(runes[:maxWidth-9]) + "..."
		}
		if summary != "" {
			result += "\n    " + dimStyle.Italic(true).Render(summary)
		}
	}

	if step.Status == PlanStepPaused && step.Error != "" {
		summary := step.Error
		if runes := []rune(summary); len(runes) > maxWidth-6 {
			summary = string(runes[:maxWidth-9]) + "..."
		}
		result += "\n    " + lipgloss.NewStyle().Foreground(ColorWarning).Italic(true).Render(summary)
	}

	if step.Status == PlanStepFailed && step.Error != "" {
		errLine := step.Error
		if runes := []rune(errLine); len(runes) > maxWidth-6 {
			errLine = string(runes[:maxWidth-9]) + "..."
		}
		result += "\n    " + lipgloss.NewStyle().Foreground(ColorError).Render("✗ "+errLine)
	}

	return result
}

// renderProgressBar renders a visual progress bar.
func (p *PlanProgressPanel) renderProgressBar(filled, width int, progress float64) string {
	// Determine color based on progress
	var barColor lipgloss.Color
	if progress >= 1.0 {
		barColor = ColorSuccess
	} else if progress >= 0.5 {
		barColor = ColorWarning
	} else {
		barColor = ColorPlan
	}

	barStyle := lipgloss.NewStyle().Foreground(barColor)
	emptyStyle := lipgloss.NewStyle().Foreground(ColorDim)

	bar := barStyle.Render(strings.Repeat("█", filled)) +
		emptyStyle.Render(strings.Repeat("░", width-filled))

	return bar
}

// firstLine returns the first non-empty line from text.
func firstLine(s string) string {
	for line := range strings.SplitSeq(s, "\n") {
		line = strings.TrimSpace(line)
		if line != "" {
			return line
		}
	}
	return ""
}

// formatElapsed formats a duration for display.
func formatElapsed(d time.Duration) string {
	if d < time.Second {
		return fmt.Sprintf("%dms", d.Milliseconds())
	}
	if d < time.Minute {
		return fmt.Sprintf("%.1fs", d.Seconds())
	}
	if d < time.Hour {
		return fmt.Sprintf("%.1fm", d.Minutes())
	}
	return fmt.Sprintf("%.1fh", d.Hours())
}

// ViewCompact renders a compact single-line version for status bar.
func (p *PlanProgressPanel) ViewCompact() string {
	if !p.visible || len(p.steps) == 0 {
		return ""
	}

	progress := p.Progress()
	completed := p.CompletedCount()
	total := len(p.steps)

	// Find current step
	currentTitle := ""
	for _, step := range p.steps {
		if step.Status == PlanStepInProgress {
			currentTitle = step.Title
			if runes := []rune(currentTitle); len(runes) > 15 {
				currentTitle = string(runes[:12]) + "..."
			}
			break
		}
	}

	// Animated icon
	var icon string
	if progress >= 1.0 {
		icon = "✓"
	} else {
		spinners := []string{"◐", "◓", "◑", "◒"}
		icon = spinners[p.frame%len(spinners)]
	}

	planStyle := lipgloss.NewStyle().Foreground(ColorPlan).Bold(true)
	mutedStyle := lipgloss.NewStyle().Foreground(ColorMuted)

	result := planStyle.Render(icon+" Plan") +
		mutedStyle.Render(fmt.Sprintf(" %d/%d", completed, total))

	if currentTitle != "" {
		result += lipgloss.NewStyle().Foreground(ColorDim).Render(" • " + currentTitle)
	}

	return result
}

// RenderStepNotification renders a notification for step status change.
func (p *PlanProgressPanel) RenderStepNotification(stepID int, status PlanStepStatus) string {
	var step *PlanStepState
	for i := range p.steps {
		if p.steps[i].ID == stepID {
			step = &p.steps[i]
			break
		}
	}

	if step == nil {
		return ""
	}

	var icon, verb string
	var color lipgloss.Color

	switch status {
	case PlanStepInProgress:
		icon = "→"
		verb = "Starting"
		color = ColorWarning
	case PlanStepCompleted:
		icon = "✓"
		verb = "Completed"
		color = ColorSuccess
	case PlanStepFailed:
		icon = "✗"
		verb = "Failed"
		color = ColorError
	case PlanStepSkipped:
		icon = "⊘"
		verb = "Skipped"
		color = ColorMuted
	default:
		return ""
	}

	style := lipgloss.NewStyle().Foreground(color)
	titleStyle := lipgloss.NewStyle().Foreground(ColorText)

	stepNum := fmt.Sprintf("[%d/%d]", stepID, len(p.steps))

	return style.Render(icon+" "+verb) + " " +
		lipgloss.NewStyle().Foreground(ColorDim).Render(stepNum) + " " +
		titleStyle.Render(step.Title)
}
