using System;
using System.Collections.Generic;
using System.Linq;
using Intrepid.Utilities;
using Sirenix.OdinInspector;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Intrepid.Core.Grounding
{
    [RequireComponent(typeof(BoxCollider))]
    public class GroundSurface : MonoBehaviour
    {
        [GroupInjections, SerializeField] private BoxCollider boxCollider;
        [GroupConfigurations("Profiles"), SerializeField] private bool safe = true;
        [GroupConfigurations("Profiles"), SerializeField] private GroundProfileMotionBehaviour motionProfile;
        [GroupConfigurations("Profiles"), SerializeField] private List<GroundProfileMovementBehaviour> movementProfiles = new();
        
        [GroupDebug, ShowInInspector, ReadOnly] public Vector3 Position { get; private set; } 
        [GroupDebug, ShowInInspector, ReadOnly] public Vector3 Normal { get; private set; }
        public GroundProfiles GroundProfiles { get; private set; } = GroundProfiles.None;

        private void OnValidate()
        {
            if (boxCollider.IsNull()) return;
            DetectPosition();
            DetectNormal();
            RefreshProfiles();
        }

        private void Awake()
        {
            DetectPosition();
            DetectNormal();
            RefreshProfiles();
        }
        
        [GroupDebug, Button(ButtonSizes.Medium)]
        private void RefreshProfiles()
        {
            var groundProfileMovement = movementProfiles.Count == 0
                ? null
                : movementProfiles
                    .Select(behaviour => behaviour.GroundProfileMovement)
                    .Aggregate(GroundProfileMovement.NoModifiers, (a, b) => a * b);
            GroundProfiles = new GroundProfiles(safe, motionProfile?.GroundProfileMotion, groundProfileMovement);
        }

        [Button(ButtonSizes.Medium)]
        private void DetectPosition()
        {
            Position = boxCollider
                .transform
                .TransformPoint(boxCollider.center);
        }
        
        [Button(ButtonSizes.Medium)]
        private void DetectNormal()
        {
            Normal = boxCollider
                .transform
                .TransformDirection(Vector3.up)
                .normalized;
        }
        
#if UNITY_EDITOR
        private void OnDrawGizmosSelected()
        {
            if (boxCollider.IsNull())
            {
                boxCollider = GetComponent<BoxCollider>();
            }

            if (boxCollider.IsNull())
            {
                return;
            }

            DetectPosition();
            DetectNormal();

            DrawColliderWire();
            DrawGroundNormal();
        }

        private void DrawColliderWire()
        {
            var previousMatrix = Gizmos.matrix;

            Gizmos.matrix = boxCollider.transform.localToWorldMatrix;
            Gizmos.color = Color.green;
            Gizmos.DrawWireCube(boxCollider.center, boxCollider.size);

            Gizmos.matrix = previousMatrix;
        }
        
        private void DrawGroundNormal()
        {
            const float normalLength = 1.25f;
            const float arrowHeadLength = 0.25f;
            const float arrowHeadAngle = 25f;

            var start = Position;
            var end = Position + Normal * normalLength;

            Handles.color = Color.magenta;
            Handles.DrawLine(start, end);

            var right = Quaternion.LookRotation(Normal) *
                        Quaternion.Euler(0f, 180f + arrowHeadAngle, 0f) *
                        Vector3.forward;

            var left = Quaternion.LookRotation(Normal) *
                       Quaternion.Euler(0f, 180f - arrowHeadAngle, 0f) *
                       Vector3.forward;

            Handles.DrawLine(end, end + right * arrowHeadLength);
            Handles.DrawLine(end, end + left * arrowHeadLength);
        }
#endif
    }
}