using System.Collections.Generic;
using System.Numerics;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Serialization;
using Quaternion = UnityEngine.Quaternion;
using Random = UnityEngine.Random;
using Vector3 = UnityEngine.Vector3;

public class ProceduralGeneration : MonoBehaviour
    {
        [FormerlySerializedAs("PrefabsList")] public List<GameObject> prefabsList = new();
        private readonly Dictionary<ModuleType, List<BuildModule>> _buildModules = new();
        public GameObject outerWall;
        
        public int maxX = 6;
        public int maxZ = 6;

        void Start()
        {
            FillDictionary();
            SpawnOuterWalls();
            Spawn(ModuleType.Floor);
            Spawn(ModuleType.Wall);
        }
        
        private void FillDictionary()
        {
            foreach (var prefab in prefabsList)
            {
                BoxCollider boxCollider = prefab.GetComponent<BoxCollider>();
                BuildModuleType buildModuleType = prefab.GetComponent<BuildModuleType>();
                
                if (boxCollider != null || buildModuleType != null)
                {
                    ModuleType moduleType = buildModuleType.moduleType;

                    Vector3 size = buildModuleType.dimensions == Vector3.zero ? boxCollider.size : buildModuleType.dimensions;
                    
                    Vector3 dimensions = new Vector3(math.floor(size.x), math.floor(size.y), math.floor(size.z));
                    
                    BuildModule buildModule = new BuildModule(dimensions, buildModuleType.offset, prefab);
                    print("Prefab dimensions: " + prefab.name + ": x - " + dimensions.x + "m, y - " + dimensions.y + "m, z - " + dimensions.z + "m");

                    AddBuildModule(moduleType, buildModule);
                }
                else
                {
                    Debug.LogWarning("Prefab " + prefab.name + " does not have a Renderer component");
                }
            }
        }

        private void AddBuildModule(ModuleType type, BuildModule module)
        {
            if (!_buildModules.ContainsKey(type))
            {
                _buildModules[type] = new List<BuildModule>();
            }

            _buildModules[type].Add(module);
        }
        
        private void Spawn(ModuleType type)
        {
            int x = 0;
            int z = 0;

            if (_buildModules.ContainsKey(type))
            {
                List<BuildModule> buildModules = _buildModules[type];

                for (int i = 0; i < (maxX * maxZ); i++)
                {
                    if (type != ModuleType.Wall || (x > 0 && x < maxX - 1 && z > 0 && z < maxZ - 1))
                    {
                        BuildModule buildModule = buildModules[Random.Range(0, buildModules.Count)];

                        Vector3 position = new Vector3(buildModule.Dimensions.x * x, 0, buildModule.Dimensions.z * z) + buildModule.Offset;

                        Quaternion rotation = Quaternion.identity;

                        if (type == ModuleType.Wall)
                        {
                            int randomRotation = Random.Range(0, 2);
                            rotation = Quaternion.Euler(0, 90 * randomRotation, 0);
                        }

                        Instantiate(buildModule.Prefab, position, rotation);
                    }

                    x++;

                    if (x >= maxZ)
                    {
                        x = 0;
                        z++;
                    }
                }
            }
        }

        private void SpawnOuterWalls()
        {
            for (int i = 1; i <= 2; i++)
            {
                var wallCount = i == 1 ? maxX : maxZ;
                var oppositeWallCount = i == 1 ? maxZ : maxX;
                var axis = i == 1 ? Vector3.forward : Vector3.right;
                var oppositeAxis = i == 1 ? Vector3.right : Vector3.forward;
                var angle = i == 1 ? 0f : 90f;
                
                for (int j = 0; j < wallCount; j++)
                {
                    Vector3 wallPosition = axis * j * 6f;
                    Quaternion wallRotation = Quaternion.Euler(0, angle, 0);
                    
                    Instantiate(outerWall, wallPosition, wallRotation);
                    Instantiate(outerWall, wallPosition + oppositeAxis * oppositeWallCount * 6f, wallRotation);
                }
            }
        }
    }