// grid.go — Renders the 8x4 Stream Deck key grid in the terminal.
//
// Uses lipgloss for styling: free slots are green with [index], occupied slots
// are dimmed with a short label (function name, icon name, or command prefix).
//
// ## Layout
//
// The grid mirrors the physical Stream Deck XL layout:
//
//	 0  1  2  3  4  5  6  7
//	 8  9 10 11 12 13 14 15
//	16 17 18 19 20 21 22 23
//	24 25 26 27 28 29 30 31
//
// ## Expanding
//
// To support non-XL models (e.g. MK.2 with 15 keys in a 5x3 grid), make
// gridCols and gridRows parameters instead of constants, and derive them
// from the device config's product_id. The device model → key count mapping
// is in internal/device/streamdeck.go.
//
// To add richer labels (e.g. showing params or the icon filename alongside
// the function name), modify keyLabel(). Keep labels under 8 chars to fit
// the cell width.

package main

import (
	"fmt"
	"strings"

	"github.com/WoodardDigital/streamdeck-go/internal/config"
	"github.com/charmbracelet/lipgloss"
)

// Grid dimensions — hardcoded for Stream Deck XL (8 columns × 4 rows = 32 keys).
const (
	gridCols = 8
	gridRows = 4
	gridKeys = gridCols * gridRows
)

// Styles for the grid cells. Color numbers are ANSI 256-color palette indices:
//
//	10 = bright green (free slots — draws the eye)
//	 8 = dark gray (occupied slots — visually recedes)
//	12 = bright blue (title)
var (
	freeStyle     = lipgloss.NewStyle().Width(9).Height(2).Align(lipgloss.Center, lipgloss.Center).Bold(true).Foreground(lipgloss.Color("10"))
	occupiedStyle = lipgloss.NewStyle().Width(9).Height(2).Align(lipgloss.Center, lipgloss.Center).Foreground(lipgloss.Color("8"))
	borderStyle   = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("8"))
	titleStyle    = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12"))
)

// renderGrid builds a string containing the full 8x4 key grid with a legend.
// Free slots show [index] in green; occupied slots show a short label in gray.
// The grid is printed to stdout before the key-picking form.
func renderGrid(keys map[int]config.KeyConfig) string {
	var b strings.Builder

	b.WriteString(titleStyle.Render("  Stream Deck XL — 8×4 grid"))
	b.WriteString("\n\n")

	for row := 0; row < gridRows; row++ {
		cells := make([]string, gridCols)
		for col := 0; col < gridCols; col++ {
			idx := row*gridCols + col
			keyCfg, occupied := keys[idx]

			if occupied {
				label := keyLabel(keyCfg)
				cells[col] = borderStyle.Render(occupiedStyle.Render(label))
			} else {
				cells[col] = borderStyle.Render(freeStyle.Render(fmt.Sprintf("[%d]", idx)))
			}
		}
		// JoinHorizontal places cells side by side, aligned at the top.
		b.WriteString(lipgloss.JoinHorizontal(lipgloss.Top, cells...))
		b.WriteString("\n")
	}

	// Legend explaining the color coding.
	b.WriteString("\n")
	b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Render("[n]"))
	b.WriteString(" = free   ")
	b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Render("dim"))
	b.WriteString(" = occupied\n")

	return b.String()
}

// keyLabel returns a short display label for an occupied key (max 8 chars).
//
// Priority order:
//  1. Module function name (if module-based key)
//  2. Icon filename without extension (if static key)
//  3. Command prefix (fallback)
//  4. "???" (shouldn't happen with valid config)
func keyLabel(k config.KeyConfig) string {
	if k.Module != "" {
		label := k.Function
		if len(label) > 8 {
			label = label[:8]
		}
		return label
	}
	if k.Icon != "" {
		label := k.Icon
		// Strip file extension for brevity (e.g. "firefox.png" → "firefox").
		if dot := strings.LastIndex(label, "."); dot > 0 {
			label = label[:dot]
		}
		if len(label) > 8 {
			label = label[:8]
		}
		return label
	}
	if k.Command != "" {
		label := k.Command
		if len(label) > 8 {
			label = label[:8]
		}
		return label
	}
	return "???"
}
