package ui

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

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	"git.leaktechnologies.dev/stu/VideoTools/internal/logging"
)

var (
	// GridColor is the color used for grid lines and borders
	GridColor color.Color
	// TextColor is the main text color
	TextColor color.Color
)

// SetColors sets the UI colors
func SetColors(grid, text color.Color) {
	GridColor = grid
	TextColor = text
}

// MonoTheme ensures all text uses a monospace font
type MonoTheme struct{}

func (m *MonoTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
	return theme.DefaultTheme().Color(name, variant)
}

func (m *MonoTheme) Font(style fyne.TextStyle) fyne.Resource {
	style.Monospace = true
	return theme.DefaultTheme().Font(style)
}

func (m *MonoTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
	return theme.DefaultTheme().Icon(name)
}

func (m *MonoTheme) Size(name fyne.ThemeSizeName) float32 {
	return theme.DefaultTheme().Size(name)
}

// ModuleTile is a clickable tile widget for module selection
type ModuleTile struct {
	widget.BaseWidget
	label     string
	color     color.Color
	enabled   bool
	onTapped  func()
	onDropped func([]fyne.URI)
}

// NewModuleTile creates a new module tile
func NewModuleTile(label string, col color.Color, enabled bool, tapped func(), dropped func([]fyne.URI)) *ModuleTile {
	m := &ModuleTile{
		label:     strings.ToUpper(label),
		color:     col,
		enabled:   enabled,
		onTapped:  tapped,
		onDropped: dropped,
	}
	m.ExtendBaseWidget(m)
	return m
}

// DraggedOver implements desktop.Droppable interface
func (m *ModuleTile) DraggedOver(pos fyne.Position) {
	logging.Debug(logging.CatUI, "DraggedOver tile=%s enabled=%v pos=%v", m.label, m.enabled, pos)
}

// Dropped implements desktop.Droppable interface
func (m *ModuleTile) Dropped(pos fyne.Position, items []fyne.URI) {
	logging.Debug(logging.CatUI, "Dropped on tile=%s enabled=%v items=%v", m.label, m.enabled, items)
	if m.enabled && m.onDropped != nil {
		logging.Debug(logging.CatUI, "Calling onDropped callback for %s", m.label)
		m.onDropped(items)
	} else {
		logging.Debug(logging.CatUI, "Drop ignored: enabled=%v hasCallback=%v", m.enabled, m.onDropped != nil)
	}
}

func (m *ModuleTile) CreateRenderer() fyne.WidgetRenderer {
	tileColor := m.color
	labelColor := TextColor

	// Dim disabled tiles
	if !m.enabled {
		// Reduce opacity by mixing with dark background
		if c, ok := m.color.(color.NRGBA); ok {
			tileColor = color.NRGBA{R: c.R / 3, G: c.G / 3, B: c.B / 3, A: c.A}
		}
		if c, ok := TextColor.(color.NRGBA); ok {
			labelColor = color.NRGBA{R: c.R / 2, G: c.G / 2, B: c.B / 2, A: c.A}
		}
	}

	bg := canvas.NewRectangle(tileColor)
	bg.CornerRadius = 8
	bg.StrokeColor = GridColor
	bg.StrokeWidth = 1

	txt := canvas.NewText(m.label, labelColor)
	txt.TextStyle = fyne.TextStyle{Monospace: true, Bold: true}
	txt.Alignment = fyne.TextAlignCenter
	txt.TextSize = 20

	return &moduleTileRenderer{
		tile:  m,
		bg:    bg,
		label: txt,
	}
}

func (m *ModuleTile) Tapped(*fyne.PointEvent) {
	if m.enabled && m.onTapped != nil {
		m.onTapped()
	}
}

type moduleTileRenderer struct {
	tile  *ModuleTile
	bg    *canvas.Rectangle
	label *canvas.Text
}

func (r *moduleTileRenderer) Layout(size fyne.Size) {
	r.bg.Resize(size)
	// Center the label by positioning it in the middle
	labelSize := r.label.MinSize()
	r.label.Resize(labelSize)
	x := (size.Width - labelSize.Width) / 2
	y := (size.Height - labelSize.Height) / 2
	r.label.Move(fyne.NewPos(x, y))
}

func (r *moduleTileRenderer) MinSize() fyne.Size {
	return fyne.NewSize(220, 110)
}

func (r *moduleTileRenderer) Refresh() {
	r.bg.FillColor = r.tile.color
	r.bg.Refresh()
	r.label.Text = r.tile.label
	r.label.Refresh()
}

func (r *moduleTileRenderer) Destroy() {}

func (r *moduleTileRenderer) Objects() []fyne.CanvasObject {
	return []fyne.CanvasObject{r.bg, r.label}
}

// TintedBar creates a colored bar container
func TintedBar(col color.Color, body fyne.CanvasObject) fyne.CanvasObject {
	rect := canvas.NewRectangle(col)
	rect.SetMinSize(fyne.NewSize(0, 48))
	padded := container.NewPadded(body)
	return container.NewMax(rect, padded)
}

// Tappable wraps any canvas object and makes it tappable
type Tappable struct {
	widget.BaseWidget
	content  fyne.CanvasObject
	onTapped func()
}

// NewTappable creates a new tappable wrapper
func NewTappable(content fyne.CanvasObject, onTapped func()) *Tappable {
	t := &Tappable{
		content:  content,
		onTapped: onTapped,
	}
	t.ExtendBaseWidget(t)
	return t
}

// CreateRenderer creates the renderer for the tappable
func (t *Tappable) CreateRenderer() fyne.WidgetRenderer {
	return &tappableRenderer{
		tappable: t,
		content:  t.content,
	}
}

// Tapped handles tap events
func (t *Tappable) Tapped(*fyne.PointEvent) {
	if t.onTapped != nil {
		t.onTapped()
	}
}

type tappableRenderer struct {
	tappable *Tappable
	content  fyne.CanvasObject
}

func (r *tappableRenderer) Layout(size fyne.Size) {
	r.content.Resize(size)
}

func (r *tappableRenderer) MinSize() fyne.Size {
	return r.content.MinSize()
}

func (r *tappableRenderer) Refresh() {
	r.content.Refresh()
}

func (r *tappableRenderer) Destroy() {}

func (r *tappableRenderer) Objects() []fyne.CanvasObject {
	return []fyne.CanvasObject{r.content}
}

// DraggableVScroll creates a vertical scroll container with draggable track
type DraggableVScroll struct {
	widget.BaseWidget
	content fyne.CanvasObject
	scroll  *container.Scroll
}

// NewDraggableVScroll creates a new draggable vertical scroll container
func NewDraggableVScroll(content fyne.CanvasObject) *DraggableVScroll {
	d := &DraggableVScroll{
		content: content,
		scroll:  container.NewVScroll(content),
	}
	d.ExtendBaseWidget(d)
	return d
}

// CreateRenderer creates the renderer for the draggable scroll
func (d *DraggableVScroll) CreateRenderer() fyne.WidgetRenderer {
	return &draggableScrollRenderer{
		scroll: d.scroll,
	}
}

// Dragged handles drag events on the scrollbar track
func (d *DraggableVScroll) Dragged(ev *fyne.DragEvent) {
	// Calculate the scroll position based on drag position
	size := d.scroll.Size()
	contentSize := d.content.MinSize()

	if contentSize.Height <= size.Height {
		return // No scrolling needed
	}

	// Calculate scroll ratio (0.0 to 1.0)
	ratio := ev.Position.Y / size.Height
	if ratio < 0 {
		ratio = 0
	}
	if ratio > 1 {
		ratio = 1
	}

	// Calculate target offset
	maxOffset := contentSize.Height - size.Height
	targetOffset := ratio * maxOffset

	// Apply scroll offset
	d.scroll.Offset = fyne.NewPos(0, targetOffset)
	d.scroll.Refresh()
}

// DragEnd handles the end of a drag event
func (d *DraggableVScroll) DragEnd() {
	// Nothing needed
}

// Tapped handles tap events on the scrollbar track
func (d *DraggableVScroll) Tapped(ev *fyne.PointEvent) {
	// Jump to tapped position
	size := d.scroll.Size()
	contentSize := d.content.MinSize()

	if contentSize.Height <= size.Height {
		return
	}

	ratio := ev.Position.Y / size.Height
	if ratio < 0 {
		ratio = 0
	}
	if ratio > 1 {
		ratio = 1
	}

	maxOffset := contentSize.Height - size.Height
	targetOffset := ratio * maxOffset

	d.scroll.Offset = fyne.NewPos(0, targetOffset)
	d.scroll.Refresh()
}

// Scrolled handles scroll events (mouse wheel)
func (d *DraggableVScroll) Scrolled(ev *fyne.ScrollEvent) {
	d.scroll.Scrolled(ev)
}

type draggableScrollRenderer struct {
	scroll *container.Scroll
}

func (r *draggableScrollRenderer) Layout(size fyne.Size) {
	r.scroll.Resize(size)
}

func (r *draggableScrollRenderer) MinSize() fyne.Size {
	return r.scroll.MinSize()
}

func (r *draggableScrollRenderer) Refresh() {
	r.scroll.Refresh()
}

func (r *draggableScrollRenderer) Destroy() {}

func (r *draggableScrollRenderer) Objects() []fyne.CanvasObject {
	return []fyne.CanvasObject{r.scroll}
}

// ConversionStatsBar shows current conversion status with live updates
type ConversionStatsBar struct {
	widget.BaseWidget
	running   int
	pending   int
	completed int
	failed    int
	progress  float64
	jobTitle  string
	onTapped  func()
}

// NewConversionStatsBar creates a new conversion stats bar
func NewConversionStatsBar(onTapped func()) *ConversionStatsBar {
	c := &ConversionStatsBar{
		onTapped: onTapped,
	}
	c.ExtendBaseWidget(c)
	return c
}

// UpdateStats updates the stats display
func (c *ConversionStatsBar) UpdateStats(running, pending, completed, failed int, progress float64, jobTitle string) {
	c.running = running
	c.pending = pending
	c.completed = completed
	c.failed = failed
	c.progress = progress
	c.jobTitle = jobTitle
	c.Refresh()
}

// CreateRenderer creates the renderer for the stats bar
func (c *ConversionStatsBar) CreateRenderer() fyne.WidgetRenderer {
	bg := canvas.NewRectangle(color.NRGBA{R: 30, G: 30, B: 30, A: 255})
	bg.CornerRadius = 4
	bg.StrokeColor = GridColor
	bg.StrokeWidth = 1

	statusText := canvas.NewText("", TextColor)
	statusText.TextStyle = fyne.TextStyle{Monospace: true}
	statusText.TextSize = 11

	progressBar := widget.NewProgressBar()

	return &conversionStatsRenderer{
		bar:         c,
		bg:          bg,
		statusText:  statusText,
		progressBar: progressBar,
	}
}

// Tapped handles tap events
func (c *ConversionStatsBar) Tapped(*fyne.PointEvent) {
	if c.onTapped != nil {
		c.onTapped()
	}
}

type conversionStatsRenderer struct {
	bar         *ConversionStatsBar
	bg          *canvas.Rectangle
	statusText  *canvas.Text
	progressBar *widget.ProgressBar
}

func (r *conversionStatsRenderer) Layout(size fyne.Size) {
	r.bg.Resize(size)

	// Layout text and progress bar
	textSize := r.statusText.MinSize()
	padding := float32(8)

	// If there's a running job, show progress bar
	if r.bar.running > 0 && r.bar.progress > 0 {
		// Show progress bar on right side
		barWidth := float32(120)
		barHeight := float32(14)
		barX := size.Width - barWidth - padding
		barY := (size.Height - barHeight) / 2

		r.progressBar.Resize(fyne.NewSize(barWidth, barHeight))
		r.progressBar.Move(fyne.NewPos(barX, barY))
		r.progressBar.Show()

		// Position text on left
		r.statusText.Move(fyne.NewPos(padding, (size.Height-textSize.Height)/2))
	} else {
		// No progress bar, center text
		r.progressBar.Hide()
		r.statusText.Move(fyne.NewPos(padding, (size.Height-textSize.Height)/2))
	}
}

func (r *conversionStatsRenderer) MinSize() fyne.Size {
	return fyne.NewSize(400, 32)
}

func (r *conversionStatsRenderer) Refresh() {
	// Update status text
	if r.bar.running > 0 {
		statusStr := ""
		if r.bar.jobTitle != "" {
			// Truncate job title if too long
			title := r.bar.jobTitle
			if len(title) > 30 {
				title = title[:27] + "..."
			}
			statusStr = title
		} else {
			statusStr = "Processing"
		}

		// Always show progress percentage when running (even if 0%)
		statusStr += " • " + formatProgress(r.bar.progress)

		if r.bar.pending > 0 {
			statusStr += " • " + formatCount(r.bar.pending, "pending")
		}

		r.statusText.Text = "▶ " + statusStr
		r.statusText.Color = color.NRGBA{R: 100, G: 220, B: 100, A: 255} // Green

		// Update progress bar (show even at 0%)
		r.progressBar.SetValue(r.bar.progress / 100.0)
		r.progressBar.Show()
	} else if r.bar.pending > 0 {
		r.statusText.Text = "⏸ " + formatCount(r.bar.pending, "queued")
		r.statusText.Color = color.NRGBA{R: 255, G: 200, B: 100, A: 255} // Yellow
		r.progressBar.Hide()
	} else if r.bar.completed > 0 || r.bar.failed > 0 {
		statusStr := "✓ "
		parts := []string{}
		if r.bar.completed > 0 {
			parts = append(parts, formatCount(r.bar.completed, "completed"))
		}
		if r.bar.failed > 0 {
			parts = append(parts, formatCount(r.bar.failed, "failed"))
		}
		statusStr += strings.Join(parts, " • ")
		r.statusText.Text = statusStr
		r.statusText.Color = color.NRGBA{R: 150, G: 150, B: 150, A: 255} // Gray
		r.progressBar.Hide()
	} else {
		r.statusText.Text = "○ No active jobs"
		r.statusText.Color = color.NRGBA{R: 100, G: 100, B: 100, A: 255} // Dim gray
		r.progressBar.Hide()
	}

	r.statusText.Refresh()
	r.progressBar.Refresh()
	r.bg.Refresh()
}

func (r *conversionStatsRenderer) Destroy() {}

func (r *conversionStatsRenderer) Objects() []fyne.CanvasObject {
	return []fyne.CanvasObject{r.bg, r.statusText, r.progressBar}
}

// Helper functions for formatting
func formatProgress(progress float64) string {
	return fmt.Sprintf("%.1f%%", progress)
}

func formatCount(count int, label string) string {
	if count == 1 {
		return fmt.Sprintf("1 %s", label)
	}
	return fmt.Sprintf("%d %s", count, label)
}
