package assets

import (
	"fmt"
	"os"

	"gitea.boner.be/bdnugget/goonscape/types"
	rl "github.com/gen2brain/raylib-go/raylib"
)

// ModelLoader handles loading and fallback for 3D models
type ModelLoader struct {
	safeMode bool
}

// NewModelLoader creates a new model loader instance
func NewModelLoader() *ModelLoader {
	return &ModelLoader{
		safeMode: os.Getenv("GOONSCAPE_SAFE_MODE") == "1",
	}
}

// IsSafeMode returns if we should avoid loading external models
func (ml *ModelLoader) IsSafeMode() bool {
	return ml.safeMode || os.Getenv("GOONSCAPE_SAFE_MODE") == "1"
}

// LoadModel attempts to load a model, returning a placeholder if it fails
func (ml *ModelLoader) LoadModel(fileName string, fallbackShape int, fallbackColor rl.Color) (rl.Model, bool, rl.Color) {
	// Don't even try to load external models in safe mode
	if ml.IsSafeMode() {
		rl.TraceLog(rl.LogInfo, "Safe mode enabled, using primitive shape instead of %s", fileName)
		return ml.createPrimitiveShape(fallbackShape), false, fallbackColor
	}

	defer func() {
		// Recover from any panics during model loading
		if r := recover(); r != nil {
			rl.TraceLog(rl.LogError, "Panic in LoadModel: %v", r)
		}
	}()

	// Try to load the model
	model := rl.LoadModel(fileName)

	// Check if the model is valid
	if model.Meshes == nil || model.Meshes.VertexCount <= 0 {
		rl.TraceLog(rl.LogWarning, "Failed to load model %s, using placeholder", fileName)
		return ml.createPrimitiveShape(fallbackShape), false, fallbackColor
	}

	// For real models, return zero color since we don't need it
	return model, true, rl.Color{}
}

// createPrimitiveShape creates a simple shape without loading external models
func (ml *ModelLoader) createPrimitiveShape(shapeType int) rl.Model {
	var mesh rl.Mesh

	switch shapeType {
	case 0: // Cube
		mesh = rl.GenMeshCube(1.0, 2.0, 1.0)
	case 1: // Sphere
		mesh = rl.GenMeshSphere(1.0, 8, 8)
	case 2: // Cylinder
		mesh = rl.GenMeshCylinder(0.8, 2.0, 8)
	case 3: // Cone
		mesh = rl.GenMeshCone(1.0, 2.0, 8)
	default: // Default to cube
		mesh = rl.GenMeshCube(1.0, 2.0, 1.0)
	}

	model := rl.LoadModelFromMesh(mesh)
	return model
}

// Helper function to load animations for a model
func loadModelAnimations(animPaths map[string]string) (types.AnimationSet, error) {
	var animSet types.AnimationSet

	// Only try to load animations if environment variable isn't set
	if os.Getenv("GOONSCAPE_DISABLE_ANIMATIONS") == "1" {
		return animSet, nil
	}

	// Load idle animations if specified
	if idlePath, ok := animPaths["idle"]; ok {
		idleAnims := rl.LoadModelAnimations(idlePath)
		if len(idleAnims) > 0 {
			animSet.Idle = idleAnims
			rl.TraceLog(rl.LogInfo, "Loaded idle animation: %s (%d frames, %f seconds)",
				idlePath, idleAnims[0].FrameCount, float32(idleAnims[0].FrameCount)/60.0)
		}
	}

	// Load walk animations if specified
	if walkPath, ok := animPaths["walk"]; ok {
		walkAnims := rl.LoadModelAnimations(walkPath)
		if len(walkAnims) > 0 {
			animSet.Walk = walkAnims
			rl.TraceLog(rl.LogInfo, "Loaded walk animation: %s (%d frames, %f seconds)",
				walkPath, walkAnims[0].FrameCount, float32(walkAnims[0].FrameCount)/60.0)
		}
	}

	return animSet, nil
}

// ValidateModel checks if a model is valid and properly loaded
func ValidateModel(model rl.Model) error {
	if model.Meshes == nil {
		return fmt.Errorf("model has nil meshes")
	}
	if model.Meshes.VertexCount <= 0 {
		return fmt.Errorf("model has invalid vertex count")
	}
	return nil
}

// CompletelyAvoidExternalModels determines if we should avoid loading external models
func CompletelyAvoidExternalModels() bool {
	return os.Getenv("GOONSCAPE_SAFE_MODE") == "1"
}

// SafeLoadModel attempts to load a model, returning a placeholder if it fails
func SafeLoadModel(fileName string, fallbackShape int, fallbackColor rl.Color) (rl.Model, bool, rl.Color) {
	loader := NewModelLoader()
	return loader.LoadModel(fileName, fallbackShape, fallbackColor)
}

func LoadModels() ([]types.ModelAsset, error) {
	// Force safe mode for now until we fix the segfault
	os.Setenv("GOONSCAPE_SAFE_MODE", "1")

	models := make([]types.ModelAsset, 0, 3)
	modelLoader := NewModelLoader()

	// Colors for the different models
	goonerColor := rl.Color{R: 255, G: 200, B: 200, A: 255} // Pinkish
	coomerColor := rl.Color{R: 200, G: 230, B: 255, A: 255} // Light blue
	shrekeColor := rl.Color{R: 180, G: 255, B: 180, A: 255} // Light green

	// If in safe mode, create all models directly without loading
	if modelLoader.IsSafeMode() {
		// Gooner model (cube)
		cube := modelLoader.createPrimitiveShape(0)
		models = append(models, types.ModelAsset{
			Model:            cube,
			YOffset:          0.0,
			PlaceholderColor: goonerColor,
		})

		// Coomer model (sphere)
		sphere := modelLoader.createPrimitiveShape(1)
		models = append(models, types.ModelAsset{
			Model:            sphere,
			YOffset:          -4.0,
			PlaceholderColor: coomerColor,
		})

		// Shreke model (cylinder)
		cylinder := modelLoader.createPrimitiveShape(2)
		models = append(models, types.ModelAsset{
			Model:            cylinder,
			YOffset:          0.0,
			PlaceholderColor: shrekeColor,
		})

		return models, nil
	}

	// The rest of the function with normal model loading
	// Load Goonion model with error handling
	var goonerModel rl.Model
	var success bool
	var modelColor rl.Color

	goonerModel, success, modelColor = modelLoader.LoadModel("resources/models/gooner/walk_no_y_transform.glb", 0, goonerColor)

	// Create animations only if model was loaded successfully
	var goonerAnims types.AnimationSet
	if success {
		goonerAnims, _ = loadModelAnimations(map[string]string{
			"idle": "resources/models/gooner/idle_no_y_transform.glb",
			"walk": "resources/models/gooner/walk_no_y_transform.glb",
		})

		// Apply transformations
		transform := rl.MatrixIdentity()
		transform = rl.MatrixMultiply(transform, rl.MatrixRotateY(180*rl.Deg2rad))
		transform = rl.MatrixMultiply(transform, rl.MatrixRotateX(-90*rl.Deg2rad))
		transform = rl.MatrixMultiply(transform, rl.MatrixScale(1.0, 1.0, 1.0))
		goonerModel.Transform = transform
	}

	// Always add a model (real or placeholder)
	models = append(models, types.ModelAsset{
		Model:            goonerModel,
		Animation:        append(goonerAnims.Idle, goonerAnims.Walk...),
		AnimFrames:       int32(len(goonerAnims.Idle) + len(goonerAnims.Walk)),
		Animations:       goonerAnims,
		YOffset:          0.0,
		PlaceholderColor: modelColor,
	})

	// Coomer model with safe loading - using a sphere shape
	var coomerModel rl.Model
	coomerModel, success, modelColor = modelLoader.LoadModel("resources/models/coomer/idle_notransy.glb", 1, coomerColor)

	if success {
		// Only load animations if the model loaded successfully
		coomerAnims, _ := loadModelAnimations(map[string]string{
			"idle": "resources/models/coomer/idle_notransy.glb",
			"walk": "resources/models/coomer/unsteadywalk_notransy.glb",
		})

		// Apply transformations
		transform := rl.MatrixIdentity()
		transform = rl.MatrixMultiply(transform, rl.MatrixRotateY(180*rl.Deg2rad))
		transform = rl.MatrixMultiply(transform, rl.MatrixRotateX(-90*rl.Deg2rad))
		transform = rl.MatrixMultiply(transform, rl.MatrixScale(1.0, 1.0, 1.0))
		coomerModel.Transform = transform

		models = append(models, types.ModelAsset{
			Model:            coomerModel,
			Animation:        append(coomerAnims.Idle, coomerAnims.Walk...),
			AnimFrames:       int32(len(coomerAnims.Idle) + len(coomerAnims.Walk)),
			Animations:       coomerAnims,
			YOffset:          -4.0,
			PlaceholderColor: rl.Color{}, // Not a placeholder
		})
	} else {
		// Add a placeholder with different shape/color
		models = append(models, types.ModelAsset{
			Model:            coomerModel,
			YOffset:          -4.0,
			PlaceholderColor: modelColor,
		})
	}

	// Shreke model with safe loading - using a cylinder shape
	var shrekeModel rl.Model
	shrekeModel, success, modelColor = modelLoader.LoadModel("resources/models/shreke.obj", 2, shrekeColor)

	if success {
		// Only proceed with texture if model loaded
		shrekeTexture := rl.LoadTexture("resources/models/shreke.png")
		if shrekeTexture.ID <= 0 {
			rl.TraceLog(rl.LogWarning, "Failed to load shreke texture")
		} else {
			rl.SetMaterialTexture(shrekeModel.Materials, rl.MapDiffuse, shrekeTexture)

			models = append(models, types.ModelAsset{
				Model:            shrekeModel,
				Texture:          shrekeTexture,
				YOffset:          0.0,
				PlaceholderColor: rl.Color{}, // Not a placeholder
			})
		}
	} else {
		// Add another placeholder with different shape/color
		models = append(models, types.ModelAsset{
			Model:            shrekeModel,
			YOffset:          0.0,
			PlaceholderColor: modelColor,
		})
	}

	if len(models) == 0 {
		return nil, fmt.Errorf("failed to load any models")
	}

	return models, nil
}

func LoadMusic(filename string) (rl.Music, error) {
	defer func() {
		// Recover from any panics during music loading
		if r := recover(); r != nil {
			rl.TraceLog(rl.LogError, "Panic in LoadMusic: %v", r)
		}
	}()

	// Skip loading music if environment variable is set
	if os.Getenv("GOONSCAPE_DISABLE_AUDIO") == "1" {
		rl.TraceLog(rl.LogInfo, "Audio disabled, skipping music loading")
		return rl.Music{}, fmt.Errorf("audio disabled")
	}

	music := rl.LoadMusicStream(filename)
	if music.Stream.Buffer == nil {
		return music, fmt.Errorf("failed to load music: %s", filename)
	}
	return music, nil
}
