package main

import (
	"fmt"
	"image/color"
	"math"
	"math/rand"

	"git.zb-server.de/ZB-Server/EscapeFromTeacher/pkg/game"
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/vector"
)

// Particle definiert ein einzelnes Partikel
type Particle struct {
	X, Y    float64
	VX, VY  float64
	Life    float64 // 0.0 bis 1.0 (1.0 = neu, 0.0 = tot)
	MaxLife float64 // Ursprüngliche Lebensdauer in Sekunden
	Size    float64
	Color   color.RGBA
	Type    string // "coin", "powerup", "landing", "death"
	Gravity bool   // Soll Gravitation angewendet werden?
	FadeOut bool   // Soll ausblenden?
}

// SpawnCoinParticles erstellt Partikel für Coin-Einsammlung
func (g *Game) SpawnCoinParticles(x, y float64) {
	g.particlesMutex.Lock()
	defer g.particlesMutex.Unlock()

	// 10-15 goldene Partikel - spawnen in alle Richtungen
	count := 10 + rand.Intn(6)
	for i := 0; i < count; i++ {
		angle := rand.Float64() * 2 * math.Pi
		speed := 2.0 + rand.Float64()*3.0

		g.particles = append(g.particles, Particle{
			X:       x,
			Y:       y,
			VX:      math.Cos(angle) * speed,
			VY:      math.Sin(angle) * speed,
			Life:    1.0,
			MaxLife: 0.5 + rand.Float64()*0.3,
			Size:    3.0 + rand.Float64()*2.0,
			Color:   color.RGBA{255, 215, 0, 255}, // Gold
			Type:    "coin",
			Gravity: true,
			FadeOut: true,
		})
	}
}

// SpawnPowerupAura erstellt Partikel-Aura um Spieler mit aktivem Powerup.
// intensity: 0.0 (schwach/auslaufend) bis 1.0 (voll aktiv) – steuert Spawn-Rate und Partikelgröße.
func (g *Game) SpawnPowerupAura(x, y float64, powerupType string, intensity float64) {
	if intensity <= 0 {
		return
	}

	g.particlesMutex.Lock()
	defer g.particlesMutex.Unlock()

	// Farbe je nach Powerup-Typ
	var particleColor color.RGBA
	switch powerupType {
	case "doublejump":
		particleColor = color.RGBA{100, 200, 255, 200} // Hellblau
	case "godmode":
		particleColor = color.RGBA{255, 215, 0, 200} // Gold
	case "magnet":
		particleColor = color.RGBA{255, 80, 220, 200} // Pink/Magenta
	default:
		particleColor = color.RGBA{200, 100, 255, 200} // Lila
	}

	// Spawn-Rate skaliert mit Intensität: bei voll aktiv ~1/3 Chance, bei fast leer ~1/10
	spawnThreshold := int(3.0/intensity + 0.5)
	if spawnThreshold < 1 {
		spawnThreshold = 1
	}
	if rand.Intn(spawnThreshold) != 0 {
		return
	}

	angle := rand.Float64() * 2 * math.Pi
	distance := 25.0 + rand.Float64()*20.0
	size := (2.5 + rand.Float64()*1.5) * intensity

	g.particles = append(g.particles, Particle{
		X:       x + math.Cos(angle)*distance,
		Y:       y + math.Sin(angle)*distance,
		VX:      math.Cos(angle) * 0.5,
		VY:      math.Sin(angle) * 0.5,
		Life:    1.0,
		MaxLife: (1.0 + rand.Float64()*0.5) * intensity,
		Size:    size,
		Color:   particleColor,
		Type:    "powerup",
		Gravity: false,
		FadeOut: true,
	})
}

// SpawnLandingParticles erstellt Staub-Partikel beim Landen
func (g *Game) SpawnLandingParticles(x, y float64) {
	g.particlesMutex.Lock()
	defer g.particlesMutex.Unlock()

	// 5-8 Staub-Partikel
	count := 5 + rand.Intn(4)
	for i := 0; i < count; i++ {
		angle := math.Pi + (rand.Float64()-0.5)*0.8 // Nach unten/seitlich
		speed := 1.0 + rand.Float64()*2.0

		g.particles = append(g.particles, Particle{
			X:       x + (rand.Float64()-0.5)*30.0,
			Y:       y + 10,
			VX:      math.Cos(angle) * speed,
			VY:      math.Sin(angle) * speed,
			Life:    1.0,
			MaxLife: 0.3 + rand.Float64()*0.2,
			Size:    2.0 + rand.Float64()*2.0,
			Color:   color.RGBA{150, 150, 150, 180}, // Grau
			Type:    "landing",
			Gravity: false,
			FadeOut: true,
		})
	}
}

// SpawnDeathParticles erstellt Explosions-Partikel beim Tod
func (g *Game) SpawnDeathParticles(x, y float64) {
	g.particlesMutex.Lock()
	defer g.particlesMutex.Unlock()

	// 20-30 rote Partikel
	count := 20 + rand.Intn(11)
	for i := 0; i < count; i++ {
		angle := rand.Float64() * 2 * math.Pi
		speed := 3.0 + rand.Float64()*5.0

		g.particles = append(g.particles, Particle{
			X:       x,
			Y:       y + 10,
			VX:      math.Cos(angle) * speed,
			VY:      math.Sin(angle) * speed,
			Life:    1.0,
			MaxLife: 0.8 + rand.Float64()*0.4,
			Size:    3.0 + rand.Float64()*3.0,
			Color:   color.RGBA{255, 50, 50, 255}, // Rot
			Type:    "death",
			Gravity: true,
			FadeOut: true,
		})
	}
	g.TriggerShake(12, 7.0)
}

// UpdateParticles aktualisiert alle Partikel
func (g *Game) UpdateParticles(dt float64) {
	g.particlesMutex.Lock()
	defer g.particlesMutex.Unlock()

	// Filtern: Nur lebende Partikel behalten
	alive := make([]Particle, 0, len(g.particles))

	for i := range g.particles {
		p := &g.particles[i]

		// Position updaten
		p.X += p.VX
		p.Y += p.VY

		// Gravitation
		if p.Gravity {
			p.VY += 0.3 // Gravitation
		}

		// Friction
		p.VX *= 0.98
		p.VY *= 0.98

		// Leben verringern
		p.Life -= dt / p.MaxLife

		// Nur behalten wenn noch am Leben
		if p.Life > 0 {
			alive = append(alive, *p)
		}
	}

	g.particles = alive
}

// RenderParticles zeichnet alle Partikel
func (g *Game) RenderParticles(screen *ebiten.Image) {
	g.particlesMutex.Lock()
	defer g.particlesMutex.Unlock()

	// Canvas-Höhe für Y-Transformation
	_, canvasH := screen.Size()

	for i := range g.particles {
		p := &g.particles[i]

		// Alpha-Wert basierend auf Leben
		alpha := uint8(255)
		if p.FadeOut {
			alpha = uint8(float64(p.Color.A) * p.Life)
		}

		col := color.RGBA{p.Color.R, p.Color.G, p.Color.B, alpha}

		// Position relativ zur Kamera und mit Y-Transformation
		screenX := float32((p.X - g.camX) * GetScaleFromHeight(canvasH))
		screenY := float32(WorldToScreenYWithHeight(p.Y, canvasH))

		// Partikel als Kreis zeichnen
		vector.DrawFilledCircle(screen, screenX, screenY, float32(p.Size), col, false)
	}
}

// DetectAndSpawnParticles prüft Game-State-Änderungen und spawnt Partikel
func (g *Game) DetectAndSpawnParticles() {
	// Kopiere relevante Daten unter kurzen Lock
	g.stateMutex.Lock()

	// Kopiere Coins
	currentCoins := make(map[string]bool, len(g.gameState.CollectedCoins))
	for k, v := range g.gameState.CollectedCoins {
		currentCoins[k] = v
	}

	// Kopiere Powerups
	currentPowerups := make(map[string]bool, len(g.gameState.CollectedPowerups))
	for k, v := range g.gameState.CollectedPowerups {
		currentPowerups[k] = v
	}

	// Kopiere Spieler
	currentPlayers := make(map[string]game.PlayerState, len(g.gameState.Players))
	for k, v := range g.gameState.Players {
		currentPlayers[k] = v
	}

	// Kopiere WorldChunks für Position-Lookup
	worldChunks := make([]game.ActiveChunk, len(g.gameState.WorldChunks))
	copy(worldChunks, g.gameState.WorldChunks)

	g.stateMutex.Unlock()

	// Ab hier ohne Lock arbeiten

	// Alte Coins entfernen, die nicht mehr in currentCoins sind (Chunk wurde entfernt)
	for key := range g.lastCollectedCoins {
		if !currentCoins[key] {
			delete(g.lastCollectedCoins, key)
		}
	}

	// 1. Prüfe neue gesammelte Coins
	for coinKey := range currentCoins {
		if !g.lastCollectedCoins[coinKey] {
			// Neuer Coin gesammelt!
			if pos := g.findObjectPosition(coinKey, worldChunks, "coin"); pos != nil {
				g.SpawnCoinParticles(pos.X, pos.Y)
				g.audio.PlayCoin() // Coin Sound abspielen
			}
			g.lastCollectedCoins[coinKey] = true
		}
	}

	// Alte Powerups entfernen
	for key := range g.lastCollectedPowerups {
		if !currentPowerups[key] {
			delete(g.lastCollectedPowerups, key)
		}
	}

	// 2. Prüfe neue gesammelte Powerups
	for powerupKey := range currentPowerups {
		if !g.lastCollectedPowerups[powerupKey] {
			// Neues Powerup gesammelt!
			if pos := g.findObjectPosition(powerupKey, worldChunks, "powerup"); pos != nil {
				g.SpawnCoinParticles(pos.X, pos.Y)
				g.audio.PlayPowerUp() // PowerUp Sound abspielen
			}
			g.lastCollectedPowerups[powerupKey] = true
		}
	}

	// 3. Prüfe Spieler-Status und spawn Aura/Death Partikel
	for playerID, player := range currentPlayers {
		lastState, existed := g.lastPlayerStates[playerID]

		// Death Partikel
		if existed && lastState.IsAlive && !player.IsAlive {
			// Berechne Spieler-Mitte
			centerX := player.X - 56 + 68 + 73/2
			centerY := player.Y - 231 + 42 + 184/2
			g.SpawnDeathParticles(centerX, centerY)
		}

		// Powerup Aura (kontinuierlich)
		if player.IsAlive && !player.IsSpectator {
			// Berechne Spieler-Mitte mit Draw-Offsets
			// DrawOffX: -56, DrawOffY: -231
			// Hitbox: OffsetX: 68, OffsetY: 42, W: 73, H: 184
			centerX := player.X - 56 + 68 + 73/2
			centerY := player.Y - 231 + 42 + 184/2

			if player.HasDoubleJump {
				// Intensität nimmt mit verbleibender Zeit ab (0..15s → 1.0..0.1)
				// Zusätzlich schwächer wenn Sprung bereits verbraucht
				intensity := player.DoubleJumpRemainingSeconds / 15.0
				if intensity > 1.0 {
					intensity = 1.0
				} else if intensity < 0.1 {
					intensity = 0.1
				}
				if player.DoubleJumpUsed {
					intensity *= 0.4
				}
				g.SpawnPowerupAura(centerX, centerY, "doublejump", intensity)
			}
			if player.HasGodMode {
				// Intensität nimmt mit verbleibender Zeit ab (0..10s → 1.0..0.1)
				intensity := player.GodModeRemainingSeconds / 10.0
				if intensity > 1.0 {
					intensity = 1.0
				} else if intensity < 0.1 {
					intensity = 0.1
				}
				g.SpawnPowerupAura(centerX, centerY, "godmode", intensity)
			}
			if player.HasMagnet {
				// Intensität nimmt mit verbleibender Zeit ab (0..8s → 1.0..0.1)
				intensity := player.MagnetRemainingSeconds / 8.0
				if intensity > 1.0 {
					intensity = 1.0
				} else if intensity < 0.1 {
					intensity = 0.1
				}
				g.SpawnPowerupAura(centerX, centerY, "magnet", intensity)
			}
		}

		// State aktualisieren
		g.lastPlayerStates[playerID] = player
	}
}

// findObjectPosition finiert die Welt-Position eines Objects (Coin/Powerup) basierend auf Key
func (g *Game) findObjectPosition(objectKey string, worldChunks []game.ActiveChunk, objectType string) *struct{ X, Y float64 } {
	for _, activeChunk := range worldChunks {
		chunkDef, exists := g.world.ChunkLibrary[activeChunk.ChunkID]
		if !exists {
			continue
		}

		for objIdx, obj := range chunkDef.Objects {
			key := fmt.Sprintf("%s_%d", activeChunk.ChunkID, objIdx)
			if key == objectKey {
				assetDef, ok := g.world.Manifest.Assets[obj.AssetID]
				if ok && assetDef.Type == objectType {
					// Berechne die Mitte der Hitbox
					centerX := activeChunk.X + obj.X + assetDef.DrawOffX + assetDef.Hitbox.OffsetX + assetDef.Hitbox.W/2
					centerY := obj.Y + assetDef.DrawOffY + assetDef.Hitbox.OffsetY + assetDef.Hitbox.H/2
					return &struct{ X, Y float64 }{
						X: centerX,
						Y: centerY,
					}
				}
			}
		}
	}
	return nil
}
