package menu

import (
	"image/color"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/inpututil"
	"github.com/hajimehoshi/ebiten/v2/text/v2"
	"github.com/hajimehoshi/ebiten/v2/vector"
	"golang.org/x/image/font/basicfont"

	"github.com/atridad/LilGuy/internal/screens"
)

var (
	basicFace       = text.NewGoXFace(basicfont.Face7x13)
	basicFaceAscent = basicFace.Metrics().HAscent
	overlayImage    *ebiten.Image
	overlaySize     struct {
		width  int
		height int
	}
)

// Menu options

func getOverlayImage(width, height int) *ebiten.Image {
	if width <= 0 || height <= 0 {
		return nil
	}
	if overlayImage == nil || overlaySize.width != width || overlaySize.height != height {
		overlayImage = ebiten.NewImage(width, height)
		overlayImage.Fill(color.RGBA{R: 0, G: 0, B: 0, A: 180})
		overlaySize.width = width
		overlaySize.height = height
	}
	return overlayImage
}

type MenuOption int

const (
	OptionResume MenuOption = iota
	OptionSave
	OptionSettings
	OptionMainMenu
	OptionQuit
	optionCount
)

// Screen modes

type menuScreen int

const (
	screenMain menuScreen = iota
	screenSettings
)

type PauseMenu struct {
	selectedIndex  int
	currentScreen  menuScreen
	settingsScreen *screens.SettingsScreen
}

type FPSCapSetting interface {
	String() string
	Cycle()
}

func NewPauseMenu() *PauseMenu {
	return &PauseMenu{
		selectedIndex:  0,
		currentScreen:  screenMain,
		settingsScreen: screens.NewSettingsScreen(),
	}
}

func (m *PauseMenu) SetFPSMonitor(enabled *bool) {
	m.settingsScreen.SetFPSMonitor(enabled)
}

func (m *PauseMenu) SetFPSCap(cap FPSCapSetting) {
	m.settingsScreen.SetFPSCap(cap)
}

func (m *PauseMenu) SetPortalVisibility(enabled *bool) {
	m.settingsScreen.SetPortalVisibility(enabled)
}

func (m *PauseMenu) SetRaycastSettings(enabled *bool, debugMode *bool) {
	m.settingsScreen.SetRaycastSettings(enabled, debugMode)
}

// Update logic

func (m *PauseMenu) Update() *MenuOption {
	if m.currentScreen == screenSettings {
		return m.updateSettings()
	}
	return m.updateMain()
}

func (m *PauseMenu) updateMain() *MenuOption {
	if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) || inpututil.IsKeyJustPressed(ebiten.KeyW) {
		m.selectedIndex--
		if m.selectedIndex < 0 {
			m.selectedIndex = int(optionCount) - 1
		}
	}
	if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) || inpututil.IsKeyJustPressed(ebiten.KeyS) {
		m.selectedIndex++
		if m.selectedIndex >= int(optionCount) {
			m.selectedIndex = 0
		}
	}

	if inpututil.IsKeyJustPressed(ebiten.KeyEnter) || inpututil.IsKeyJustPressed(ebiten.KeySpace) {
		selected := MenuOption(m.selectedIndex)
		if selected == OptionSettings {
			m.currentScreen = screenSettings
			m.selectedIndex = 0
			return nil
		}
		return &selected
	}

	return nil
}

func (m *PauseMenu) updateSettings() *MenuOption {
	if m.settingsScreen.Update() {
		m.currentScreen = screenMain
		m.selectedIndex = 0
	}
	return nil
}

// Rendering

func (m *PauseMenu) Draw(screen *ebiten.Image, screenWidth, screenHeight int) {
	if overlay := getOverlayImage(screenWidth, screenHeight); overlay != nil {
		screen.DrawImage(overlay, nil)
	}

	menuWidth := 400
	menuHeight := 380
	menuX := (screenWidth - menuWidth) / 2
	menuY := (screenHeight - menuHeight) / 2

	vector.FillRect(screen,
		float32(menuX), float32(menuY),
		float32(menuWidth), float32(menuHeight),
		color.RGBA{R: 40, G: 40, B: 50, A: 255},
		false,
	)

	vector.StrokeRect(screen,
		float32(menuX), float32(menuY),
		float32(menuWidth), float32(menuHeight),
		2,
		color.RGBA{R: 100, G: 100, B: 120, A: 255},
		false,
	)

	if m.currentScreen == screenSettings {
		m.drawSettings(screen, menuX, menuY, menuWidth, menuHeight)
	} else {
		m.drawMain(screen, menuX, menuY, menuWidth, menuHeight)
	}
}

func (m *PauseMenu) drawMain(screen *ebiten.Image, menuX, menuY, menuWidth, menuHeight int) {
	titleText := "PAUSED"
	titleX := menuX + (menuWidth / 2) - (len(titleText) * 7 / 2)
	titleY := menuY + 50
	m.drawText(screen, titleText, color.White, titleX, titleY)

	options := []string{"Resume", "Save", "Settings", "Main Menu", "Quit"}
	startY := menuY + 90

	for i, option := range options {
		optionY := startY + (i * 45)
		optionX := menuX + (menuWidth / 2) - (len(option) * 7 / 2)

		if i == m.selectedIndex {
			indicatorX := optionX - 20
			m.drawText(screen, ">", color.RGBA{R: 255, G: 200, B: 0, A: 255}, indicatorX, optionY)
			m.drawText(screen, option, color.RGBA{R: 255, G: 255, B: 100, A: 255}, optionX, optionY)
		} else {
			m.drawText(screen, option, color.RGBA{R: 180, G: 180, B: 180, A: 255}, optionX, optionY)
		}
	}

	// Instructions
	hintText := "Use Arrow Keys/WASD to navigate, Enter/Space to select"
	hintX := menuX + (menuWidth / 2) - (len(hintText) * 7 / 2)
	hintY := menuY + menuHeight - 30
	m.drawText(screen, hintText, color.RGBA{R: 150, G: 150, B: 150, A: 255}, hintX, hintY)
}

func (m *PauseMenu) drawSettings(screen *ebiten.Image, menuX, menuY, menuWidth, menuHeight int) {
	vector.FillRect(screen,
		float32(menuX), float32(menuY),
		float32(menuWidth), float32(menuHeight),
		color.RGBA{R: 40, G: 40, B: 50, A: 255},
		false,
	)

	vector.StrokeRect(screen,
		float32(menuX), float32(menuY),
		float32(menuWidth), float32(menuHeight),
		2,
		color.RGBA{R: 100, G: 100, B: 120, A: 255},
		false,
	)

	screenWidth := menuWidth
	screenHeight := menuHeight

	subScreen := ebiten.NewImage(screenWidth, screenHeight)
	m.settingsScreen.Draw(subScreen, screenWidth, screenHeight, "SETTINGS")

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(float64(menuX), float64(menuY))
	screen.DrawImage(subScreen, op)
}

func (m *PauseMenu) drawText(screen *ebiten.Image, txt string, clr color.Color, x, y int) {
	op := &text.DrawOptions{}
	op.GeoM.Translate(float64(x), float64(y)-basicFaceAscent)
	op.ColorScale.ScaleWithColor(clr)
	text.Draw(screen, txt, basicFace, op)
}

func (m *PauseMenu) Reset() {
	m.selectedIndex = 0
	m.currentScreen = screenMain
	m.settingsScreen.Reset()
}
