// Package layout provides canvas and drawing utilities for hdmistat
package layout

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"log/slog"

	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
)

const (
	// Display constants
	defaultDPI     = 72
	percentDivisor = 100.0
	halfDivisor    = 2

	// Time constants
	secondsPerDay    = 86400
	secondsPerHour   = 3600
	secondsPerMinute = 60

	// Byte formatting constants
	byteUnit = 1024
)

// Canvas provides a simple API for rendering text and graphics
type Canvas struct {
	img    *image.RGBA
	font   *truetype.Font
	logger *slog.Logger
}

// TextStyle defines text rendering parameters
type TextStyle struct {
	Size      float64
	Color     color.Color
	Alignment Alignment
	Bold      bool
}

// Alignment for text rendering
type Alignment int

const (
	// AlignLeft aligns text to the left
	AlignLeft Alignment = iota
	// AlignCenter centers text
	AlignCenter
	// AlignRight aligns text to the right
	AlignRight
)

// Point represents a 2D coordinate
type Point struct {
	X, Y int
}

// NewCanvas creates a new canvas for drawing
func NewCanvas(width, height int, font *truetype.Font, logger *slog.Logger) *Canvas {
	img := image.NewRGBA(image.Rect(0, 0, width, height))

	// Fill with black background
	draw.Draw(img, img.Bounds(), &image.Uniform{color.Black}, image.Point{}, draw.Src)

	return &Canvas{
		img:    img,
		font:   font,
		logger: logger,
	}
}

// Clear fills the canvas with a color
func (c *Canvas) Clear(col color.Color) {
	draw.Draw(c.img, c.img.Bounds(), &image.Uniform{col}, image.Point{}, draw.Src)
}

// DrawText renders text at the specified position
func (c *Canvas) DrawText(text string, pos Point, style TextStyle) error {
	if style.Color == nil {
		style.Color = color.White
	}

	ctx := freetype.NewContext()
	ctx.SetDPI(defaultDPI)
	ctx.SetFont(c.font)
	ctx.SetFontSize(style.Size)
	ctx.SetClip(c.img.Bounds())
	ctx.SetDst(c.img)
	ctx.SetSrc(&image.Uniform{style.Color})

	// Calculate text bounds for alignment
	opts := truetype.Options{
		Size: style.Size,
		DPI:  defaultDPI,
	}
	face := truetype.NewFace(c.font, &opts)
	bounds, _ := font.BoundString(face, text)
	width := bounds.Max.X - bounds.Min.X

	x := pos.X
	switch style.Alignment {
	case AlignCenter:
		x = pos.X - width.Round()/halfDivisor
	case AlignRight:
		x = pos.X - width.Round()
	}

	pt := freetype.Pt(x, pos.Y)
	_, err := ctx.DrawString(text, pt)
	return err
}

// DrawTextMultiline renders multiple lines of text
func (c *Canvas) DrawTextMultiline(lines []string, pos Point, style TextStyle, lineSpacing float64) error {
	y := pos.Y
	for _, line := range lines {
		if err := c.DrawText(line, Point{X: pos.X, Y: y}, style); err != nil {
			return err
		}
		y += int(style.Size * lineSpacing)
	}
	return nil
}

// DrawBox draws a filled rectangle
func (c *Canvas) DrawBox(x, y, width, height int, col color.Color) {
	rect := image.Rect(x, y, x+width, y+height)
	draw.Draw(c.img, rect, &image.Uniform{col}, image.Point{}, draw.Src)
}

// DrawBorder draws a rectangle border
func (c *Canvas) DrawBorder(x, y, width, height, thickness int, col color.Color) {
	// Top
	c.DrawBox(x, y, width, thickness, col)
	// Bottom
	c.DrawBox(x, y+height-thickness, width, thickness, col)
	// Left
	c.DrawBox(x, y, thickness, height, col)
	// Right
	c.DrawBox(x+width-thickness, y, thickness, height, col)
}

// DrawProgress draws a progress bar
func (c *Canvas) DrawProgress(x, y, width, height int, percent float64, fg, bg color.Color) {
	// Background
	c.DrawBox(x, y, width, height, bg)

	// Foreground
	fillWidth := int(float64(width) * percent / percentDivisor)
	if fillWidth > 0 {
		c.DrawBox(x, y, fillWidth, height, fg)
	}

	// Border
	c.DrawBorder(x, y, width, height, 1, color.Gray{128})
}

// DrawHLine draws a horizontal line
func (c *Canvas) DrawHLine(x, y, width int, col color.Color) {
	c.DrawBox(x, y, width, 1, col)
}

// DrawVLine draws a vertical line
func (c *Canvas) DrawVLine(x, y, height int, col color.Color) {
	c.DrawBox(x, y, 1, height, col)
}

// Image returns the underlying image
func (c *Canvas) Image() *image.RGBA {
	return c.img
}

// Size returns the canvas dimensions
func (c *Canvas) Size() (width, height int) {
	bounds := c.img.Bounds()
	return bounds.Dx(), bounds.Dy()
}

// FormatBytes formats byte counts for display
func FormatBytes(bytes uint64) string {
	if bytes < byteUnit {
		return fmt.Sprintf("%d B", bytes)
	}
	div, exp := uint64(byteUnit), 0
	for n := bytes / byteUnit; n >= byteUnit; n /= byteUnit {
		div *= byteUnit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}

// FormatDuration formats time durations for display
func FormatDuration(d float64) string {
	seconds := int(d)
	days := seconds / secondsPerDay
	hours := (seconds % secondsPerDay) / secondsPerHour
	minutes := (seconds % secondsPerHour) / secondsPerMinute

	if days > 0 {
		return fmt.Sprintf("%dd %dh %dm", days, hours, minutes)
	} else if hours > 0 {
		return fmt.Sprintf("%dh %dm", hours, minutes)
	}
	return fmt.Sprintf("%dm", minutes)
}
