//nolint:mnd
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"git.eeqj.de/sneak/hdmistat/internal/fbdraw"
	"git.eeqj.de/sneak/hdmistat/internal/layout"
)

const (
	// DefaultFontSize is the font size used throughout the application
	DefaultFontSize = 24
)

// HelloWorldScreen implements the FrameGenerator interface
type HelloWorldScreen struct {
	width  int
	height int
}

// NewHelloWorldScreen creates a new hello world screen
func NewHelloWorldScreen() *HelloWorldScreen {
	return &HelloWorldScreen{}
}

// Init initializes the screen with the display dimensions
func (h *HelloWorldScreen) Init(width, height int) error {
	h.width = width
	h.height = height
	return nil
}

// getSystemUptime returns the system uptime
func getSystemUptime() (time.Duration, error) {
	data, err := os.ReadFile("/proc/uptime")
	if err != nil {
		return 0, err
	}

	fields := strings.Fields(string(data))
	if len(fields) < 1 {
		return 0, fmt.Errorf("invalid /proc/uptime format")
	}

	seconds, err := strconv.ParseFloat(fields[0], 64)
	if err != nil {
		return 0, err
	}

	return time.Duration(seconds * float64(time.Second)), nil
}

// GenerateFrame generates a frame with "Hello World" and a timestamp
func (h *HelloWorldScreen) GenerateFrame(grid *fbdraw.CharGrid) error {
	// Create a draw context that works directly on the provided grid
	draw := layout.NewDraw(grid)

	// Clear the screen with a dark background
	draw.Clear()

	// Calculate center position
	centerY := grid.Height / 2

	// Draw "Hello World" in the center
	draw.Color(layout.Color("cyan")).Bold()
	draw.TextCenter(0, centerY-2, "Hello World")

	// Draw current time below in RFC format
	draw.Color(layout.Color("white")).Plain()
	currentTime := time.Now().Format(time.RFC1123)
	draw.TextCenter(0, centerY, "%s", currentTime)

	// Draw system uptime below that
	uptime, err := getSystemUptime()
	if err != nil {
		uptime = 0
	}
	draw.Color(layout.Color("gray60"))
	draw.TextCenter(
		0,
		centerY+2,
		"System Uptime: %s",
		formatDuration(uptime),
	)

	// Add a decorative border
	borderGrid := draw.Grid(2, 2, grid.Width-4, grid.Height-4)
	borderGrid.Border(layout.Color("gray30"))

	return nil
}

// FramesPerSecond returns the desired frame rate (1 FPS for clock updates)
func (h *HelloWorldScreen) FramesPerSecond() float64 {
	return 1.0
}

// formatDuration formats a duration in Go duration string format
func formatDuration(d time.Duration) string {
	return d.Round(time.Second).String()
}

func main() {
	// Set up signal handling
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		<-sigChan
		log.Println("Received shutdown signal")
		cancel()
	}()

	// Create framebuffer display
	display, err := fbdraw.NewFBDisplayAuto()
	if err != nil {
		log.Fatalf("Failed to open framebuffer: %v", err)
	}
	defer func() {
		if err := display.Close(); err != nil {
			log.Printf("Failed to close display: %v", err)
		}
	}()

	// Create carousel with no rotation (single screen)
	carousel := fbdraw.NewCarousel(display, 0) // 0 means no rotation

	// Set font size
	if err := carousel.SetFontSize(DefaultFontSize); err != nil {
		log.Fatalf("Failed to set font size: %v", err)
	}

	// Add our hello world screen with header
	helloScreen := NewHelloWorldScreen()
	wrappedScreen := fbdraw.NewHeaderWrapper(helloScreen)

	if err := carousel.AddScreen("Hello World", wrappedScreen); err != nil {
		log.Fatalf("Failed to add screen: %v", err)
	}

	// Run the carousel
	log.Println("Starting fbhello...")
	log.Println("Press Ctrl+C to exit")

	// Run carousel in a goroutine
	done := make(chan error, 1)
	go func() {
		done <- carousel.Run()
	}()

	// Wait for either context cancellation or carousel to finish
	select {
	case <-ctx.Done():
		log.Println("Stopping carousel...")
		carousel.Stop()
		<-done // Wait for carousel to finish
	case err := <-done:
		if err != nil {
			log.Printf("Carousel error: %v", err)
		}
	}

	log.Println("fbhello exited cleanly")
}
