package fbdraw

import (
	"fmt"
	"image"
	"image/color"
	"strings"
	"sync"

	"git.eeqj.de/sneak/hdmistat/internal/font"
	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
)

// Common colors
//
//nolint:gochecknoglobals
var (
	Black   = color.RGBA{0, 0, 0, 255}
	White   = color.RGBA{255, 255, 255, 255}
	Red     = color.RGBA{255, 0, 0, 255}
	Green   = color.RGBA{0, 255, 0, 255}
	Blue    = color.RGBA{0, 0, 255, 255}
	Yellow  = color.RGBA{255, 255, 0, 255}
	Cyan    = color.RGBA{0, 255, 255, 255}
	Magenta = color.RGBA{255, 0, 255, 255}
	Orange  = color.RGBA{255, 165, 0, 255}
	Purple  = color.RGBA{128, 0, 128, 255}

	// Grays
	Gray10 = color.RGBA{26, 26, 26, 255}
	Gray20 = color.RGBA{51, 51, 51, 255}
	Gray30 = color.RGBA{77, 77, 77, 255}
	Gray40 = color.RGBA{102, 102, 102, 255}
	Gray50 = color.RGBA{128, 128, 128, 255}
	Gray60 = color.RGBA{153, 153, 153, 255}
	Gray70 = color.RGBA{179, 179, 179, 255}
	Gray80 = color.RGBA{204, 204, 204, 255}
	Gray90 = color.RGBA{230, 230, 230, 255}
)

// Cell represents a single character cell in the grid
type Cell struct {
	Rune       rune
	Foreground color.Color
	Background color.Color
	Weight     font.FontWeight
	Italic     bool
}

// CharGrid represents a monospace character grid
type CharGrid struct {
	Width  int      // Width in characters
	Height int      // Height in characters
	Cells  [][]Cell // 2D array [y][x]

	// Font settings
	FontFamily font.FontFamily
	FontSize   float64 // Points

	// Computed values
	CharWidth  int // Pixel width of a character
	CharHeight int // Pixel height of a character

	// Rendering cache
	fontCache map[fontKey]*truetype.Font
	mu        sync.RWMutex
}

type fontKey struct {
	family font.FontFamily
	weight font.FontWeight
	italic bool
}

// NewCharGrid creates a new character grid
func NewCharGrid(width, height int) *CharGrid {
	// Create 2D array
	cells := make([][]Cell, height)
	for y := 0; y < height; y++ {
		cells[y] = make([]Cell, width)
		// Initialize with spaces and default colors
		for x := 0; x < width; x++ {
			cells[y][x] = Cell{
				Rune:       ' ',
				Foreground: White,
				Background: Black,
				Weight:     font.WeightRegular,
				Italic:     false,
			}
		}
	}

	return &CharGrid{
		Width:      width,
		Height:     height,
		Cells:      cells,
		FontFamily: font.FamilyIBMPlexMono,
		FontSize:   14,
		CharWidth:  8,  // Will be computed based on font
		CharHeight: 16, // Will be computed based on font
		fontCache:  make(map[fontKey]*truetype.Font),
	}
}

// SetCell sets a single cell's content
func (g *CharGrid) SetCell(x, y int, r rune, fg, bg color.Color, weight font.FontWeight, italic bool) {
	if x < 0 || x >= g.Width || y < 0 || y >= g.Height {
		return
	}

	g.Cells[y][x] = Cell{
		Rune:       r,
		Foreground: fg,
		Background: bg,
		Weight:     weight,
		Italic:     italic,
	}
}

// WriteString writes a string starting at position (x, y)
func (g *CharGrid) WriteString(x, y int, s string, fg, bg color.Color, weight font.FontWeight, italic bool) {
	runes := []rune(s)
	for i, r := range runes {
		g.SetCell(x+i, y, r, fg, bg, weight, italic)
	}
}

// Clear clears the grid with the specified background color
func (g *CharGrid) Clear(bg color.Color) {
	for y := 0; y < g.Height; y++ {
		for x := 0; x < g.Width; x++ {
			g.Cells[y][x] = Cell{
				Rune:       ' ',
				Foreground: White,
				Background: bg,
				Weight:     font.WeightRegular,
				Italic:     false,
			}
		}
	}
}

// getFont retrieves a font from cache or loads it
func (g *CharGrid) getFont(weight font.FontWeight, italic bool) (*truetype.Font, error) {
	key := fontKey{
		family: g.FontFamily,
		weight: weight,
		italic: italic,
	}

	g.mu.RLock()
	if f, ok := g.fontCache[key]; ok {
		g.mu.RUnlock()
		return f, nil
	}
	g.mu.RUnlock()

	// Load font
	g.mu.Lock()
	defer g.mu.Unlock()

	// Double-check after acquiring write lock
	if f, ok := g.fontCache[key]; ok {
		return f, nil
	}

	f, err := font.LoadFont(g.FontFamily, weight, italic)
	if err != nil {
		return nil, err
	}

	g.fontCache[key] = f
	return f, nil
}

// computeCharSize computes the character cell size based on font metrics
func (g *CharGrid) computeCharSize() error {
	f, err := g.getFont(font.WeightRegular, false)
	if err != nil {
		return err
	}

	// Use freetype to measure a typical character
	opts := truetype.Options{
		Size: g.FontSize,
		DPI:  72,
	}
	face := truetype.NewFace(f, &opts)

	// Measure 'M' for width (typically widest regular character in monospace)
	bounds, _, _ := face.GlyphBounds('M')
	g.CharWidth = (bounds.Max.X - bounds.Min.X).Round()

	// Use font metrics for height
	metrics := face.Metrics()
	g.CharHeight = (metrics.Ascent + metrics.Descent).Round()

	return nil
}

// Render renders the grid to an image
func (g *CharGrid) Render() (*image.RGBA, error) {
	// Ensure character dimensions are computed
	if err := g.computeCharSize(); err != nil {
		return nil, err
	}

	// Create image
	width := g.Width * g.CharWidth
	height := g.Height * g.CharHeight
	img := image.NewRGBA(image.Rect(0, 0, width, height))

	// First pass: draw backgrounds
	for y := 0; y < g.Height; y++ {
		for x := 0; x < g.Width; x++ {
			cell := g.Cells[y][x]

			// Draw background rectangle
			x0 := x * g.CharWidth
			y0 := y * g.CharHeight
			x1 := x0 + g.CharWidth
			y1 := y0 + g.CharHeight

			for py := y0; py < y1; py++ {
				for px := x0; px < x1; px++ {
					img.Set(px, py, cell.Background)
				}
			}
		}
	}

	// Second pass: draw text
	ctx := freetype.NewContext()
	ctx.SetDPI(72)
	ctx.SetFont(nil) // Will be set per cell
	ctx.SetFontSize(g.FontSize)
	ctx.SetClip(img.Bounds())
	ctx.SetDst(img)

	for y := 0; y < g.Height; y++ {
		for x := 0; x < g.Width; x++ {
			cell := g.Cells[y][x]

			if cell.Rune == ' ' {
				continue // Skip spaces
			}

			// Get font for this cell
			f, err := g.getFont(cell.Weight, cell.Italic)
			if err != nil {
				continue // Skip cells with font errors
			}

			ctx.SetFont(f)
			ctx.SetSrc(image.NewUniform(cell.Foreground))

			// Calculate text position
			// X: left edge of cell
			// Y: baseline (ascent from top of cell)
			opts := truetype.Options{
				Size: g.FontSize,
				DPI:  72,
			}
			face := truetype.NewFace(f, &opts)
			metrics := face.Metrics()

			px := x * g.CharWidth
			py := y*g.CharHeight + metrics.Ascent.Round()

			pt := freetype.Pt(px, py)
			_, _ = ctx.DrawString(string(cell.Rune), pt)
		}
	}

	return img, nil
}

// ToText renders the grid as text for debugging/logging
func (g *CharGrid) ToText() string {
	var sb strings.Builder

	for y := 0; y < g.Height; y++ {
		for x := 0; x < g.Width; x++ {
			sb.WriteRune(g.Cells[y][x].Rune)
		}
		if y < g.Height-1 {
			sb.WriteRune('\n')
		}
	}

	return sb.String()
}

// ToANSI renders the grid as ANSI escape sequences for terminal display
func (g *CharGrid) ToANSI() string {
	var sb strings.Builder

	// Track last colors to minimize escape sequences
	var lastFg, lastBg color.Color
	var lastWeight font.FontWeight
	var lastItalic bool

	for y := 0; y < g.Height; y++ {
		for x := 0; x < g.Width; x++ {
			cell := g.Cells[y][x]

			// Update styles if changed
			if cell.Foreground != lastFg || cell.Background != lastBg ||
				cell.Weight != lastWeight || cell.Italic != lastItalic {

				// Reset
				sb.WriteString("\033[0m")

				// Weight
				if cell.Weight == font.WeightBold ||
					cell.Weight == font.WeightExtraBold ||
					cell.Weight == font.WeightBlack {
					sb.WriteString("\033[1m") // Bold
				}

				// Italic
				if cell.Italic {
					sb.WriteString("\033[3m")
				}

				// Foreground color
				if r, g, b, _ := cell.Foreground.RGBA(); r != 0 || g != 0 || b != 0 {
					sb.WriteString(fmt.Sprintf("\033[38;2;%d;%d;%dm",
						r>>8, g>>8, b>>8))
				}

				// Background color
				if r, g, b, _ := cell.Background.RGBA(); r != 0 || g != 0 || b != 0 {
					sb.WriteString(fmt.Sprintf("\033[48;2;%d;%d;%dm",
						r>>8, g>>8, b>>8))
				}

				lastFg = cell.Foreground
				lastBg = cell.Background
				lastWeight = cell.Weight
				lastItalic = cell.Italic
			}

			sb.WriteRune(cell.Rune)
		}

		// Reset at end of line and add newline
		sb.WriteString("\033[0m\n")
		lastFg = nil
		lastBg = nil
		lastWeight = ""
		lastItalic = false
	}

	// Final reset
	sb.WriteString("\033[0m")

	return sb.String()
}

// GridWriter provides a convenient API for writing to a grid
type GridWriter struct {
	Grid       *CharGrid
	X, Y       int // Current position
	Foreground color.Color
	Background color.Color
	Weight     font.FontWeight
	Italic     bool
}

// NewGridWriter creates a new GridWriter
func NewGridWriter(grid *CharGrid) *GridWriter {
	return &GridWriter{
		Grid:       grid,
		X:          0,
		Y:          0,
		Foreground: White,
		Background: Black,
		Weight:     font.WeightRegular,
		Italic:     false,
	}
}

// MoveAbs moves the cursor to an absolute position
func (w *GridWriter) MoveAbs(x, y int) *GridWriter {
	w.X = x
	w.Y = y
	return w
}

// Move moves the cursor relative to the current position
func (w *GridWriter) Move(dx, dy int) *GridWriter {
	w.X += dx
	w.Y += dy
	return w
}

// SetColor sets the foreground color
func (w *GridWriter) SetColor(c color.Color) *GridWriter {
	w.Foreground = c
	return w
}

// SetBackground sets the background color
func (w *GridWriter) SetBackground(c color.Color) *GridWriter {
	w.Background = c
	return w
}

// SetWeight sets the font weight
func (w *GridWriter) SetWeight(weight font.FontWeight) *GridWriter {
	w.Weight = weight
	return w
}

// SetItalic sets italic style
func (w *GridWriter) SetItalic(italic bool) *GridWriter {
	w.Italic = italic
	return w
}

// Write writes a string at the current position
func (w *GridWriter) Write(format string, args ...interface{}) *GridWriter {
	s := fmt.Sprintf(format, args...)
	w.Grid.WriteString(w.X, w.Y, s, w.Foreground, w.Background, w.Weight, w.Italic)
	w.X += len([]rune(s))
	return w
}

// WriteLine writes a string and moves to the next line
func (w *GridWriter) WriteLine(format string, args ...interface{}) *GridWriter {
	w.Write(format, args...)
	w.X = 0
	w.Y++
	return w
}

// NewLine moves to the next line
func (w *GridWriter) NewLine() *GridWriter {
	w.X = 0
	w.Y++
	return w
}

// Clear clears the grid with the current background color
func (w *GridWriter) Clear() *GridWriter {
	w.Grid.Clear(w.Background)
	w.X = 0
	w.Y = 0
	return w
}

// DrawMeter draws a progress meter at the current position
func (w *GridWriter) DrawMeter(percent float64, width int) *GridWriter {
	if percent < 0 {
		percent = 0
	}
	if percent > 100 {
		percent = 100
	}

	filled := int(percent / 100.0 * float64(width))

	// Save original color
	origColor := w.Foreground

	// Set color based on percentage
	if percent > 80 {
		w.SetColor(Red)
	} else if percent > 50 {
		w.SetColor(Yellow)
	} else {
		w.SetColor(Green)
	}

	// Draw the meter
	for i := 0; i < width; i++ {
		if i < filled {
			w.Write("█")
		} else {
			w.Write("▒")
		}
	}

	// Restore original color
	w.SetColor(origColor)

	return w
}
