package tui

import (
	"fmt"
	"strings"

	"github.com/charmbracelet/lipgloss"
)

const statGap = 5
const spacer = ""

// View renders the current state of the Model as a string for display
func (m Model) View() string {
	if m.showResults {
		return m.renderResults()
	}

	var sections []string

	timer := m.renderTimer()
	sections = append(sections, timer)

	textDisplay := m.renderText()
	sections = append(sections, textDisplay)

	content := lipgloss.JoinVertical(lipgloss.Left, sections...)

	return lipgloss.Place(
		m.width, m.height,
		lipgloss.Center, lipgloss.Center,
		content,
	)
}

// renderTimer formats the remaining time for display
func (m Model) renderTimer() string {
	remaining := m.game.GetRemainingTime()
	return timeStyle.Render(fmt.Sprintf("%d", remaining))
}

// renderText formats the text display with appropriate styles for typed, current, untyped characters
func (m Model) renderText() string {
	displayText := m.game.GetDisplayText()

	var rendered strings.Builder

	for i, char := range displayText {
		// Use helper to style character
		styledChar := m.styleChar(char, i)
		rendered.WriteString(styledChar)
	}

	// Format into lines
	content := rendered.String()
	lines := m.formatIntoLines(content)

	return textBoxStyle.Render(strings.Join(lines, "\n"))
}

// formatIntoLines formats the content into lines based on the game's display settings
func (m Model) formatIntoLines(plainContent string) []string {
	lines := m.game.DisplayLines

	maxLines := m.game.LinesPerView
	if len(lines) > maxLines {
		lines = lines[:maxLines]
	}

	var styledLines []string
	charIndex := 0

	for i, line := range lines {
		if i >= maxLines {
			break
		}

		var styledLine strings.Builder

		lineRunes := []rune(line)

		for col := 0; col < len(lineRunes); col++ {
			if charIndex < len(plainContent) {
				styledChar := m.styleChar(lineRunes[col], charIndex)
				styledLine.WriteString(styledChar)
				charIndex++
			} else {
				styledLine.WriteString(mutedStyle.Render(string(lineRunes[col])))
			}
		}

		// Check if caret is on this line and positioned just beyond last char
		caretPos := m.game.CurrentPos
		if i == 0 && caretPos == len(lineRunes) {
			// Append caret style with a space or block to show cursor
			styledLine.WriteString(cursorStyle.Render(" "))
		}

		styledLines = append(styledLines, styledLine.String())

		// Advance charIndex as before for spacing between lines
		if charIndex < len(plainContent) && i < len(lines)-1 {
			charIndex++
		}
	}

	return styledLines
}

// styleChar determines the style of a character based on its position and error status
func (m Model) styleChar(char rune, index int) string {
	userPos := m.game.CurrentPos
	errorIndex := m.game.GlobalPos - (userPos - index)

	switch {
	case index < userPos:
		// Already typed
		if m.game.Errors != nil {
			if _, hasErr := m.game.Errors[errorIndex]; hasErr {
				return errorStyle.Render(string(char))
			}
		}
		return boldStyle.Render(string(char))
	case index == userPos:
		// Current character
		return cursorStyle.Render(string(char))
	default:
		// Not yet typed
		return mutedStyle.Render(string(char))
	}
}

// renderResults formats the final results of the typing test for display
func (m Model) renderResults() string {
	stats := m.finalStats

	accSection := lipgloss.JoinVertical(
		lipgloss.Right,
		mutedStyle.Render("acc"),
		boldStyle.Render(fmt.Sprintf("%.0f%%", stats.Accuracy)),
	)

	wpmSection := lipgloss.JoinVertical(
		lipgloss.Right,
		mutedStyle.Render("wpm"),
		boldStyle.Render(fmt.Sprintf("%.0f", stats.WPM)),
	)

	timeSection := lipgloss.JoinVertical(
		lipgloss.Right,
		mutedStyle.Render("time"),
		boldStyle.Render(fmt.Sprintf("%.0fs", stats.TimeElapsed.Seconds())),
	)

	languageSection := lipgloss.JoinVertical(
		lipgloss.Right,
		mutedStyle.Render("lang"),
		boldStyle.Render(m.language),
	)

	// Arrange stats horizontally
	statsRow := lipgloss.JoinHorizontal(
		lipgloss.Top,
		accSection,
		strings.Repeat(" ", statGap),
		wpmSection,
		strings.Repeat(" ", statGap),
		timeSection,
		strings.Repeat(" ", statGap),
		languageSection,
	)

	instructions := mutedStyle.Align(lipgloss.Center).Render("Press Enter to restart • Esc to quit")

	// Results layout
	resultsContent := lipgloss.JoinVertical(
		lipgloss.Center,
		spacer,
		statsRow,
		spacer,
		instructions,
	)

	return lipgloss.Place(
		m.width, m.height,
		lipgloss.Center, lipgloss.Center,
		resultsContainerStyle.Render(resultsContent),
	)
}
