package scenes

import (
	"image"
	"image/color"
	_ "image/jpeg"
	_ "image/png"
	"log"

	"mountain/internal/entities"

	"github.com/go-gl/mathgl/mgl64"
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)

const (
	Gravity   = 0.5
	JumpForce = -8.0
	MoveSpeed = 4.0
	ViewSize  = 380 // 800 width, we have 2 viewports (380 each with 20 padding)
)

type Player struct {
	Pos mgl64.Vec3 // 3D Kordinater: X (vänster/höger), Y (upp/ner), Z (djup)
	Vel mgl64.Vec3 // Hastighet

	Width, Height float64 // För (AABB)
	IsGrounded    bool

	// Sprites
	SpriteRun        *ebiten.Image
	SpriteIdle       *ebiten.Image
	SpriteTop        *ebiten.Image
	AnimationFrame   int
	AnimationCounter int

	FacingRight bool
}

type PlayScene struct {
	world     *entities.World
	player    *Player
	leftView  *ebiten.Image
	rightView *ebiten.Image
}

func NewPlayScene() *PlayScene {
	w, err := entities.LoadWorldFromTiled("assets/maps/room1.tmj")
	if err != nil {
		log.Println("Kunde inte ladda room1:", err)
		w = entities.NewWorld(100, 10, 100) // Fallback tom värd om JSON saknas
	}

	pRun, _, err := ebitenutil.NewImageFromFile("assets/images/Warrior_1/Run.png")
	if err != nil {
		log.Println("Run sprite error:", err)
		pRun = ebiten.NewImage(96, 96)
	}
	pIdle, _, _ := ebitenutil.NewImageFromFile("assets/images/Warrior_1/Idle.png")
	if pIdle == nil {
		pIdle = pRun
	}

	pTop := ebiten.NewImage(96, 96)
	pTop.Fill(color.RGBA{255, 255, 0, 255})

	player := &Player{
		Pos:         mgl64.Vec3{50.0 * entities.TileSize, 6.0 * entities.TileSize, 50.0 * entities.TileSize},
		Width:       96,
		Height:      96,
		SpriteRun:   pRun,
		SpriteIdle:  pIdle,
		SpriteTop:   pTop,
		FacingRight: true,
	}

	return &PlayScene{
		world:     w,
		player:    player,
		leftView:  ebiten.NewImage(ViewSize, ViewSize),
		rightView: ebiten.NewImage(ViewSize, ViewSize),
	}
}

func (s *PlayScene) Update() error {
	p := s.player

	p.Vel[0] = 0
	p.Vel[2] = 0
	isMoving := false

	if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) {
		p.Vel[0] = -MoveSpeed
		p.FacingRight = false
		isMoving = true
	}
	if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) {
		p.Vel[0] = MoveSpeed
		p.FacingRight = true
		isMoving = true
	}
	if ebiten.IsKeyPressed(ebiten.KeyUp) || ebiten.IsKeyPressed(ebiten.KeyW) {
		p.Vel[2] = -MoveSpeed
		isMoving = true
	}
	if ebiten.IsKeyPressed(ebiten.KeyDown) || ebiten.IsKeyPressed(ebiten.KeyS) {
		p.Vel[2] = MoveSpeed
		isMoving = true
	}

	if ebiten.IsKeyPressed(ebiten.KeySpace) && p.IsGrounded {
		p.Vel[1] = JumpForce
		p.IsGrounded = false
	}

	if !p.IsGrounded {
		p.Vel[1] += Gravity
	}

	nextX := p.Pos[0] + p.Vel[0]
	nextY := p.Pos[1] + p.Vel[1]
	nextZ := p.Pos[2] + p.Vel[2]

	gridY := int((nextY + p.Height) / entities.TileSize)
	gridX := int((nextX + p.Width/2) / entities.TileSize)
	gridZ := int((nextZ + p.Width/2) / entities.TileSize)

	entY := s.world.GetEntityAt(gridX, gridY, gridZ)
	if entY != nil && entY.IsBlocking() && p.Vel[1] >= 0 {
		nextY = float64(gridY)*entities.TileSize - p.Height - 0.1
		p.Vel[1] = 0
		p.IsGrounded = true
	} else {
		p.IsGrounded = false
	}

	p.Pos[0] = nextX
	p.Pos[1] = nextY
	p.Pos[2] = nextZ

	// Kolla ifall vi står på en portal
	standEnt := s.world.GetEntityAt(gridX, gridY, gridZ)
	if prt, ok := standEnt.(*entities.Portal); ok {
		log.Println("Byter rum till: ", prt.TargetMap)
		newW, err := entities.LoadWorldFromTiled("assets/maps/" + prt.TargetMap + ".tmj")
		if err == nil {
			s.world = newW
			// Reset player position for new map
			if prt.TargetMap == "room1" {
				p.Pos = mgl64.Vec3{80.0 * entities.TileSize, 6.0 * entities.TileSize, 10.0 * entities.TileSize}
			} else {
				p.Pos = mgl64.Vec3{40.0 * entities.TileSize, 6.0 * entities.TileSize, 65.0 * entities.TileSize}
			}
		}
	}

	if isMoving {
		p.AnimationCounter++
		if p.AnimationCounter > 5 {
			p.AnimationFrame = (p.AnimationFrame + 1) % 6
			p.AnimationCounter = 0
		}
	} else {
		p.AnimationFrame = 0
	}

	return nil
}

func (s *PlayScene) Draw(screen *ebiten.Image) {
	screen.Fill(color.RGBA{20, 20, 20, 255})

	s.leftView.Fill(color.RGBA{135, 206, 235, 255})
	s.rightView.Fill(color.RGBA{50, 150, 50, 255})

	playerGridZ := int((s.player.Pos[2] + s.player.Width/2) / entities.TileSize)
	playerGridY := int((s.player.Pos[1] + s.player.Height/2) / entities.TileSize)

	camX := s.player.Pos[0] - float64(ViewSize)/2.0
	camY := s.player.Pos[1] - float64(ViewSize)/2.0
	camZ := s.player.Pos[2] - float64(ViewSize)/2.0

	// Vänster vy: Sido-vy (renderar objekt vid spelarens Z och "inåt", max 3 djup)
	// Rendera från djupast till spelarens Z (mindre Z är djupare inåt bild/scen)
	leftDepthStart := playerGridZ - 3
	if leftDepthStart < 0 {
		leftDepthStart = 0
	}
	for zLevel := leftDepthStart; zLevel <= playerGridZ; zLevel++ {
		tint := float32(1.0)
		alpha := float32(1.0)
		if zLevel < playerGridZ {
			// Förskugga och gör transparent lager längre "in"
			diff := float32(playerGridZ - zLevel)
			tint = 1.0 - (diff * 0.2)
			alpha = 1.0 - (diff * 0.25)
			if tint < 0.2 {
				tint = 0.2
			}
			if alpha < 0.2 {
				alpha = 0.2
			}
		}

		for x := 0; x < s.world.Width; x++ {
			for y := 0; y < s.world.Height; y++ {
				ent := s.world.GetEntityAt(x, y, zLevel)
				if ent != nil {
					px := float64(x)*entities.TileSize - camX
					py := float64(y)*entities.TileSize - camY
					ent.DrawSide(s.leftView, px, py, tint, alpha)
				}
			}
		}
	}

	// Höger vy: Ovan-vy (renderar ner i marken så att det känns som det går "in" i bilden)
	// -1: Två block inåt jord/hål (Djupast)
	// 0: Ett block inåt jord/hål
	// +1: Samma (eller 1 över)
	// För att saker under spelaren ska ligga bakom byter vi tecken. "upp" i z/y är nu inåt.
	// Vi visar tre block nedåt i Y: playerGridY+3, Y+2, Y+1, Y
	topLevels := []int{playerGridY + 3, playerGridY + 2, playerGridY + 1, playerGridY}
	for i, yLevel := range topLevels {
		if yLevel >= 0 && yLevel < s.world.Height {
			tint := float32(1.0)
			alpha := float32(1.0)

			// Sätt färger baserat på relativ nivå ("inåt") där de djupaste (Y+3) är mörkare
			// Index 0 är djupast i marken
			if i == 0 { // -2 block (långt under fötter)
				tint = 0.6
				alpha = 0.4
			} else if i == 1 { // -1 block (nyss under fötter)
				tint = 0.8
				alpha = 0.6
			} else if i == 2 { // marken spelaren står på
				tint = 1.0
				alpha = 1.0
			} else if i == 3 { // spelarens fothöjd (0)
				tint = 1.2
				alpha = 1.0
			}

			for x := 0; x < s.world.Width; x++ {
				for z := 0; z < s.world.Depth; z++ {
					ent := s.world.GetEntityAt(x, yLevel, z)
					if ent != nil {
						px := float64(x)*entities.TileSize - camX
						pz := float64(z)*entities.TileSize - camZ
						ent.DrawTop(s.rightView, px, pz, tint, alpha)
					}
				}
			}
		}

		// Rendera spelaren på toppen (efter marken och spelarens lager är ritat)
		if i == 3 {
			s.drawPlayerTop(s.rightView, camX, camZ)
		}
	}

	s.drawPlayerSide(s.leftView, camX, camY)

	opL := &ebiten.DrawImageOptions{}
	opL.GeoM.Translate(10, 100)
	screen.DrawImage(s.leftView, opL)

	opR := &ebiten.DrawImageOptions{}
	opR.GeoM.Translate(410, 100)
	screen.DrawImage(s.rightView, opR)

	ebitenutil.DebugPrint(screen, "VÄNSTER (Sido vy)        HÖGER (Top vy: Slice av din Hojd)")
}

func (s *PlayScene) drawPlayerSide(screen *ebiten.Image, camX, camY float64) {
	p := s.player

	sx := p.AnimationFrame * 96
	srcRect := image.Rect(sx, 0, sx+96, 96)
	var activeSprite *ebiten.Image

	if p.Vel[0] != 0 || p.Vel[2] != 0 { // Is moving X or Z
		activeSprite = p.SpriteRun
	} else {
		activeSprite = p.SpriteIdle
	}

	subImg := activeSprite.SubImage(srcRect).(*ebiten.Image)

	op := &ebiten.DrawImageOptions{}
	if !p.FacingRight {
		op.GeoM.Scale(-1, 1)
		op.GeoM.Translate(96, 0)
	}

	drawX := p.Pos[0] - camX
	drawY := p.Pos[1] - camY

	op.GeoM.Translate(drawX, drawY)
	screen.DrawImage(subImg, op)
}

func (s *PlayScene) drawPlayerTop(screen *ebiten.Image, camX, camZ float64) {
	p := s.player
	op := &ebiten.DrawImageOptions{}

	drawX := p.Pos[0] - camX
	drawZ := p.Pos[2] - camZ
	op.GeoM.Translate(drawX, drawZ)
	screen.DrawImage(p.SpriteTop, op)
}
