using SolarSystemUI;
using Unity.VisualScripting;
using UnityEngine;
using Utils;

namespace SolarSystem
{
    public class PlanetController : MonoBehaviour
    {
        public GameObject StarPrefab;
        public GameObject PlanetaryRingsPrefab;
        public GameObject AxialTiltMarkerPrefab;
        public GameObject SpinDirectionMarkerPrefab;
        public bool ReverseOrbitDirection;
        
        // LIGHT DEBUGGING
        public GameObject LightMarkerPrefab;
        private GameObject _lightMarker;
        // LIGHT DEBUGGING
        
        private float _elapsedTime;
        private GameObject _planetaryRings;
        private GameObject _axialTiltMarker;
        private GameObject _spinDirectionMarker;
        private UI_DebugMarkers _uiDebugMarkers;
        private PlanetInfo _planetInfo;
        private Rigidbody _rigidBody;
        private Light _planetLight;
        private float _distanceFromPlanet;
        private MaterialPropertyBlock _materialPropertyBlock;
        private Vector3 _startingPosition;
        private Vector3 _orbitCenter;
        private float _orbitRadius;

        private static readonly int _innerRadius = Shader.PropertyToID("_InnerRadius");
        private static readonly int _ringColor = Shader.PropertyToID("_RingColor");
        private static readonly int BaseMap = Shader.PropertyToID("_BaseMap");
        private static readonly int MainTexture = Shader.PropertyToID("_MainTexture");

        public void Start()
        {
            _planetInfo = GetComponent<PlanetInfo>();
            _rigidBody = GetComponent<Rigidbody>();
            _uiDebugMarkers = GameObject.Find("UI_DebugMarkers").GetComponent<UI_DebugMarkers>();
            
            _materialPropertyBlock = new MaterialPropertyBlock();
            AssignMaterialProperties();
            
            SetGOStartingPositionRotation();
            SetGOScale();
            _distanceFromPlanet = transform.localScale.x + 2f;
            SetGOAxialTiltMarker();
            SetGOSpinDirectionMarker();
            //TrySetGOPlanetaryRings();
            
            _orbitCenter = StarPrefab.transform.position;
            _orbitRadius = Vector3.Distance(transform.position, _orbitCenter);

            // Calculate initial elapsed time based on the starting position (for orbit)
            float initialAngle = Mathf.Atan2(transform.position.z - _orbitCenter.z, transform.position.x - _orbitCenter.x);
            _elapsedTime = initialAngle / Mathf.Deg2Rad / (360.0f / _planetInfo.OrbitalPeriod);
            
            SetGOInitialComps();
            
            // Instantiate and set up the light marker
            if (LightMarkerPrefab != null)
            {
                _lightMarker = Instantiate(LightMarkerPrefab);
            }
        }

        public void Update()
        {
            ToggleMarkerVisibility();
        }

        private void FixedUpdate()
        {
            CalculatePlanetaryLightPosition();
            
            // Update the light marker position
            if (_lightMarker is not null && _planetLight is not null)
            {
                _lightMarker.transform.position = _planetLight.transform.position;
            }
            
            if (GameSpeedController.Instance is null) return;
            OrbitAroundStar();
            RotateAroundOwnAxis();
        }
        
        private void AssignMaterialProperties()
        {
            switch (_planetInfo.PlanetType)
            {
                case "RockyPlanet":
                {
                    Texture2D[] texturesRP = Resources.LoadAll<Texture2D>("Textures/RockyPlanet");
                    if (texturesRP.Length == 0)
                    {
                        Debug.LogWarning("No textures found in `Resources/Textures/RockyPlanet`");
                        return;
                    }

                    Texture2D randomTexture = texturesRP[Random.Range(0, texturesRP.Length)];
                    _materialPropertyBlock.SetTexture(BaseMap, randomTexture);
                    GetComponent<MeshRenderer>().SetPropertyBlock(_materialPropertyBlock);
                    break;
                }
                case "GasGiant":
                {
                    Texture2D[] texturesGG = Resources.LoadAll<Texture2D>("Textures/GasGiant");
                    if (texturesGG.Length == 0)
                    {
                        Debug.LogWarning("No textures found in `Resources/Textures/GasGiant`");
                        return;
                    }

                    Texture2D randomTexture = texturesGG[Random.Range(0, texturesGG.Length)];
                    _materialPropertyBlock.SetTexture(MainTexture, randomTexture);
                    GetComponent<MeshRenderer>().SetPropertyBlock(_materialPropertyBlock);
                    break;
                }
            }
        }

        private void SetGOInitialComps()
        {
            if (_rigidBody is null) return;
            _rigidBody.mass = _planetInfo.Mass;
            _rigidBody.constraints = RigidbodyConstraints.FreezePositionY;
            _rigidBody.useGravity = false;

            if (_planetInfo == null) return;
            _planetLight = transform.AddComponent<Light>();
            _planetLight.cullingMask = 1 << LayerMask.NameToLayer(_planetInfo.LayerName);
            _planetLight.type = LightType.Directional;
            _planetLight.color = Color.white;
            _planetLight.intensity = 1f;
        }

        private void SetGOScale()
        {
            if (transform.parent == null) return;
            Vector3 desiredWorldScale = new (_planetInfo.GO_Radius, _planetInfo.GO_Radius, _planetInfo.GO_Radius);
            Vector3 parentScale = transform.parent.localScale;
            transform.localScale = new Vector3(
                desiredWorldScale.x / parentScale.x,
                desiredWorldScale.y / parentScale.y,
                desiredWorldScale.z / parentScale.z
            );
        }

        private void SetGOStartingPositionRotation()
        {
            float radius = _planetInfo.Index;
            float angle = Random.Range(0f, Mathf.PI * 2);
            float xComponent = Mathf.Cos(angle) * radius;
            float zComponent = Mathf.Sin(angle) * radius;

            _startingPosition = new Vector3(xComponent, 0f, zComponent);
            transform.position = _startingPosition;
            transform.rotation = Quaternion.Euler(0, 0, _planetInfo.AxialTilt);
        }
        
        private void SetGOAxialTiltMarker()
        {
            _axialTiltMarker = Instantiate(AxialTiltMarkerPrefab, transform.position, Quaternion.identity);
            _axialTiltMarker.transform.parent = transform;
            _axialTiltMarker.transform.localPosition = Vector3.zero;
            _axialTiltMarker.transform.localScale = new Vector3(1.0f, 1.0f, 0.0f);
            _axialTiltMarker.transform.localRotation = Quaternion.Euler(-_planetInfo.AxialTilt, 0f, 0f);
        }

        private void SetGOSpinDirectionMarker()
        {
            _spinDirectionMarker = Instantiate(SpinDirectionMarkerPrefab, transform.position, Quaternion.identity);
            _spinDirectionMarker.transform.parent = transform;
            _spinDirectionMarker.transform.localPosition = Vector3.zero;
            _spinDirectionMarker.transform.localScale = new Vector3(1.0f, 1.0f, 0.0f);
            _spinDirectionMarker.transform.localRotation = Quaternion.Euler(0f, 0f, 0f);
        }

        private void TrySetGOPlanetaryRings() // TODO: FIX THIS SHIT
        {
            if (!_planetInfo.HasRings) return;

            _planetaryRings = Instantiate(PlanetaryRingsPrefab, transform.position, Quaternion.identity);
            _planetaryRings.transform.parent = transform;
            _planetaryRings.transform.localPosition = Vector3.zero;

            float minInner = _planetInfo.Info_Radius + 3f;
            float maxInner = _planetInfo.Info_Radius + 7.5f;
            _planetInfo.InnerRingRadius = Mathf.InverseLerp(minInner, maxInner, _planetInfo.InnerRingRadius);
            _planetInfo.InnerRingRadius = Mathf.Clamp(_planetInfo.InnerRingRadius, 0.1f, 0.4f);
            _planetaryRings.transform.localScale = new Vector3(_planetInfo.OuterRingRadius, 0f, _planetInfo.OuterRingRadius);
            _planetaryRings.transform.localRotation = _planetaryRings.transform.parent.localRotation;
            _planetaryRings.transform.GetComponent<MeshRenderer>().material.SetFloat(_innerRadius, _planetInfo.InnerRingRadius);
            _planetaryRings.transform.GetComponent<MeshRenderer>().material.SetColor(_ringColor, TryGenerateRandomRingsColor());
        }

        private static Color TryGenerateRandomRingsColor()
        {
            float r = Random.Range(0.8f, 1.0f);
            float g = Random.Range(0.7f, 0.74f);
            float b = Random.Range(0.53f, 0.57f);
            return new Color(r, g, b);
        }

        private void RotateAroundOwnAxis()
        {
            transform.Rotate(Vector3.up, Time.fixedDeltaTime * GameSpeedController.Instance.CurSpeed);
        }

        private void OrbitAroundStar()
        {
            _elapsedTime += Time.fixedDeltaTime * GameSpeedController.Instance.CurSpeed;
            float angle = (_elapsedTime * (ReverseOrbitDirection ? -1 : 1) * 360.0f / ((_planetInfo.OrbitalPeriod / ConstantsUtil.EARTH_YEAR) / 0.01f));
            float radians = angle * Mathf.Deg2Rad;

            Vector3 newPos = new(
                _orbitCenter.x + Mathf.Cos(radians) * _orbitRadius,
                _orbitCenter.y,
                _orbitCenter.z + Mathf.Sin(radians) * _orbitRadius
            );

            transform.position = newPos;
        }

        private void CalculatePlanetaryLightPosition()
        {
            Vector3 directionToStar = (StarPrefab.transform.position - transform.position).normalized;
            _planetLight.transform.position = transform.position + directionToStar * _distanceFromPlanet;
            _planetLight.transform.LookAt(transform);
        }

        private void ToggleMarkerVisibility()
        {
            if (_uiDebugMarkers is null) return;
            _axialTiltMarker?.SetActive(_uiDebugMarkers.ShowAxialTiltMarkers);
            _spinDirectionMarker?.SetActive(_uiDebugMarkers.ShowSpinDirectionMarkers);
        }
    }
}