package screens

import (
	"fmt"
	"image/color"
	"time"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	"github.com/hajimehoshi/ebiten/v2/vector"

	"github.com/atridad/LilGuy/internal/hero"
	"github.com/atridad/LilGuy/internal/projectile"
	"github.com/atridad/LilGuy/internal/save"
	"github.com/atridad/LilGuy/internal/status"
	"github.com/atridad/LilGuy/internal/ui/hud"
	"github.com/atridad/LilGuy/internal/world"
)

// Screen and hero defaults
var (
	backgroundColor       = color.NRGBA{R: 135, G: 206, B: 235, A: 255}
	saveNotificationColor = color.NRGBA{R: 50, G: 200, B: 50, A: 255}
)

const (
	heroStartX       = 960 / 2
	heroStartY       = 540 / 2
	heroRadius       = 28.0
	heroSpeed        = 180.0
	heroMaxStamina   = 100.0
	heroStaminaDrain = 50.0
	heroStaminaRegen = 30.0

	saveNotificationDuration = 2 * time.Second
)

var (
	heroColor = color.NRGBA{R: 210, G: 220, B: 255, A: 255}
)

// HUD positioning and colors
const (
	hudX = 960 - 220
	hudY = 20
)

var (
	staminaNormalColor = color.NRGBA{R: 0, G: 255, B: 180, A: 255}
	staminaLowColor    = color.NRGBA{R: 255, G: 60, B: 60, A: 255}
	fpsGoodColor       = color.NRGBA{R: 120, G: 255, B: 120, A: 255}
	fpsWarnColor       = color.NRGBA{R: 255, G: 210, B: 100, A: 255}
	fpsPoorColor       = color.NRGBA{R: 255, G: 120, B: 120, A: 255}
)

// FPS monitoring thresholds
const (
	staminaLowThreshold = 0.2
	fpsWarnThreshold    = 0.85
	fpsPoorThreshold    = 0.6
	fpsSampleWindow     = time.Second
	targetTPS           = 60
)

type GameplayInput struct {
	Left   bool
	Right  bool
	Jump   bool
	Sprint bool
	Shoot  bool
}

type GameplayScreen struct {
	hero           *hero.Hero
	hud            hud.Overlay
	world          *world.World
	projectiles    *projectile.Manager
	bounds         hero.Bounds
	lastTick       time.Time
	gameStartTime  time.Time
	totalPlayTime  time.Duration
	fpsEnabled     *bool
	fpsFrames      int
	fpsAccumulator time.Duration
	fpsValue       float64

	saveNotificationTimer time.Duration
	showSaveNotification  bool
}

func NewGameplayScreen(screenWidth, screenHeight int, fpsEnabled *bool) *GameplayScreen {
	w := world.NewWorld()

	groundHeight := 16.0
	w.AddSurface(&world.Surface{
		X:      0,
		Y:      float64(screenHeight) - groundHeight,
		Width:  float64(screenWidth),
		Height: groundHeight,
		Tag:    world.TagGround,
		Color:  color.NRGBA{R: 34, G: 139, B: 34, A: 255},
	})

	return &GameplayScreen{
		hero: hero.New(hero.Config{
			StartX:       heroStartX,
			StartY:       heroStartY,
			Radius:       heroRadius,
			Speed:        heroSpeed,
			Color:        heroColor,
			MaxStamina:   heroMaxStamina,
			StaminaDrain: heroStaminaDrain,
			StaminaRegen: heroStaminaRegen,
		}),
		hud: hud.Overlay{
			X:     hudX,
			Y:     hudY,
			Color: color.White,
		},
		world:       w,
		projectiles: projectile.NewManager(),
		bounds: hero.Bounds{
			Width:  float64(screenWidth),
			Height: float64(screenHeight),
			Ground: float64(screenHeight) - groundHeight,
		},
		lastTick:      time.Now(),
		gameStartTime: time.Now(),
		fpsEnabled:    fpsEnabled,
	}
}

func (g *GameplayScreen) Update(input GameplayInput, delta time.Duration) {
	dt := delta.Seconds()

	g.hero.Update(hero.Input{
		Left:   input.Left,
		Right:  input.Right,
		Jump:   input.Jump,
		Sprint: input.Sprint,
	}, dt, g.bounds)

	if input.Shoot {
		direction := 1.0
		if g.hero.GetDirection() == hero.DirLeft {
			direction = -1.0
		}
		g.projectiles.Shoot(g.hero.X, g.hero.Y-20, direction, g.hero.ProjectileConfig)
	}

	g.projectiles.Update(dt, g.bounds.Width, g.bounds.Height)

	g.totalPlayTime += delta

	g.trackFPS(delta)

	if g.showSaveNotification {
		g.saveNotificationTimer -= delta
		if g.saveNotificationTimer <= 0 {
			g.showSaveNotification = false
		}
	}
}

// FPS tracking
func (g *GameplayScreen) trackFPS(delta time.Duration) {
	if g.fpsEnabled == nil || !*g.fpsEnabled {
		return
	}

	g.fpsAccumulator += delta
	g.fpsFrames++

	if g.fpsAccumulator >= fpsSampleWindow {
		g.fpsValue = float64(g.fpsFrames) / g.fpsAccumulator.Seconds()
		g.fpsAccumulator = 0
		g.fpsFrames = 0
	}
}

// Rendering
func (g *GameplayScreen) Draw(screen *ebiten.Image) {
	screen.Fill(backgroundColor)

	g.world.Draw(screen)
	g.projectiles.Draw(screen)
	g.hero.Draw(screen)

	staminaColor := staminaNormalColor
	if g.hero.Stamina < g.hero.MaxStamina*staminaLowThreshold {
		staminaColor = staminaLowColor
	}

	staminaMeter := status.Meter{
		Label: "Stamina",
		Base:  g.hero.MaxStamina,
		Level: g.hero.Stamina,
		Color: staminaColor,
	}

	meters := []status.Meter{staminaMeter}

	if g.fpsEnabled != nil && *g.fpsEnabled {
		ratio := g.fpsValue / float64(targetTPS)
		fpsColor := fpsGoodColor
		switch {
		case ratio < fpsPoorThreshold:
			fpsColor = fpsPoorColor
		case ratio < fpsWarnThreshold:
			fpsColor = fpsWarnColor
		}

		fpsMeter := status.Meter{
			Label: fmt.Sprintf("Framerate: %3.0f FPS", g.fpsValue),
			Base:  -1,
			Level: 0,
			Color: fpsColor,
		}
		meters = append(meters, fpsMeter)
	}

	g.hud.Draw(screen, meters)

	if g.showSaveNotification {
		g.drawSaveNotification(screen)
	}
}

func (g *GameplayScreen) drawSaveNotification(screen *ebiten.Image) {
	centerX := float32(g.bounds.Width / 2)
	centerY := float32(30)

	boxWidth := float32(140)
	boxHeight := float32(40)

	vector.DrawFilledRect(screen,
		centerX-boxWidth/2,
		centerY-boxHeight/2,
		boxWidth,
		boxHeight,
		color.NRGBA{R: 0, G: 0, B: 0, A: 180},
		false)

	vector.StrokeRect(screen,
		centerX-boxWidth/2,
		centerY-boxHeight/2,
		boxWidth,
		boxHeight,
		2,
		saveNotificationColor,
		false)

	msg := "Game Saved!"
	textX := centerX - 45
	textY := centerY - 5
	ebitenutil.DebugPrintAt(screen, msg, int(textX), int(textY))
}

func (g *GameplayScreen) ShowSaveNotification() {
	g.showSaveNotification = true
	g.saveNotificationTimer = saveNotificationDuration
}

// State management
func (g *GameplayScreen) Reset() {
	screenWidth := int(g.bounds.Width)
	screenHeight := int(g.bounds.Height)
	groundHeight := 16.0

	w := world.NewWorld()
	w.AddSurface(&world.Surface{
		X:      0,
		Y:      float64(screenHeight) - groundHeight,
		Width:  float64(screenWidth),
		Height: groundHeight,
		Tag:    world.TagGround,
		Color:  color.NRGBA{R: 34, G: 139, B: 34, A: 255},
	})

	g.hero = hero.New(hero.Config{
		StartX:       heroStartX,
		StartY:       heroStartY,
		Radius:       heroRadius,
		Speed:        heroSpeed,
		Color:        heroColor,
		MaxStamina:   heroMaxStamina,
		StaminaDrain: heroStaminaDrain,
		StaminaRegen: heroStaminaRegen,
	})
	g.world = w
	g.projectiles = projectile.NewManager()
	g.bounds = hero.Bounds{
		Width:  float64(screenWidth),
		Height: float64(screenHeight),
		Ground: float64(screenHeight) - groundHeight,
	}
	g.lastTick = time.Now()
	g.gameStartTime = time.Now()
	g.totalPlayTime = 0
	g.fpsFrames = 0
	g.fpsAccumulator = 0
	g.fpsValue = 0
}

func (g *GameplayScreen) SaveState() *save.GameState {
	return &save.GameState{
		HeroX:       g.hero.X,
		HeroY:       g.hero.Y,
		HeroStamina: g.hero.Stamina,
		PlayTimeMS:  g.totalPlayTime.Milliseconds(),
	}
}

func (g *GameplayScreen) LoadState(state *save.GameState) {
	screenWidth := int(g.bounds.Width)
	screenHeight := int(g.bounds.Height)
	groundHeight := 16.0

	w := world.NewWorld()
	w.AddSurface(&world.Surface{
		X:      0,
		Y:      float64(screenHeight) - groundHeight,
		Width:  float64(screenWidth),
		Height: groundHeight,
		Tag:    world.TagGround,
		Color:  color.NRGBA{R: 34, G: 139, B: 34, A: 255},
	})

	g.hero = hero.New(hero.Config{
		StartX:       state.HeroX,
		StartY:       state.HeroY,
		Radius:       heroRadius,
		Speed:        heroSpeed,
		Color:        heroColor,
		MaxStamina:   heroMaxStamina,
		StaminaDrain: heroStaminaDrain,
		StaminaRegen: heroStaminaRegen,
	})
	g.hero.Stamina = state.HeroStamina
	g.world = w
	g.projectiles = projectile.NewManager()
	g.bounds = hero.Bounds{
		Width:  float64(screenWidth),
		Height: float64(screenHeight),
		Ground: float64(screenHeight) - groundHeight,
	}
	g.totalPlayTime = time.Duration(state.PlayTimeMS) * time.Millisecond
	g.lastTick = time.Now()
	g.gameStartTime = time.Now()
	g.fpsFrames = 0
	g.fpsAccumulator = 0
	g.fpsValue = 0
}
