using UnityEngine;

namespace eagle {
    /// <summary>
    /// Spawns a small group of animated 3D animals that wander randomly within a radius.
    /// Each animal independently idles, walks to random points, and occasionally eats or attacks.
    /// Uses the Animal Pack Deluxe animator state names (idle, walk, run, eat, attack).
    /// </summary>
    [RequireComponent(typeof(RectTransform))]
    public class AnimalEffect : MonoBehaviour {
        [Header("Animal Prefabs")]
        [Tooltip("One or more prefabs; each spawned animal picks randomly from this list")]
        public GameObject[] animalPrefabs;

        [Header("Group Settings")]
        public int animalCount = 3;
        public float wanderRadius = 25f;
        public float moveSpeed = 15f;
        public float animalScale = 15f;
        [System.NonSerialized]
        public bool clipToViewport = true;

        [Header("Animator Override")]
        [Tooltip(
                "If set, replaces the prefab's animator controller at spawn time (applies to all prefabs)")]
        public RuntimeAnimatorController animatorController;

        [Tooltip(
                "Per-prefab controller overrides (index matches animalPrefabs); takes priority over animatorController")]
        public RuntimeAnimatorController[] animatorControllers;

        [Header("Behavior Timing")]
        public float minIdleDuration = 1f;
        public float maxIdleDuration = 3f;
        [Tooltip("Chance of performing eat/attack after arriving (0-1)")]
        public float actionChance = 0.3f;
        public float actionDuration = 2f;
        public float turnSpeed = 5f;
        [Tooltip("Most imported 3D beast models face out their local -Z axis.")]
        public bool modelFrontFacesNegativeZ = true;

        private enum AnimalStateType { Idle, Walking, Action }

        private class AnimalState {
            public GameObject instance;
            public Animator animator;
            public AnimalStateType state;
            public Vector3 targetPosition;
            public float timer;
        }

        private AnimalState[] _animals;

        private static readonly int IdleHash = Animator.StringToHash("idle");
        private static readonly int WalkHash = Animator.StringToHash("walk");
        private static readonly int EatHash = Animator.StringToHash("eat");
        private static readonly int AttackHash = Animator.StringToHash("attack");
        private static readonly int DieHash = Animator.StringToHash("die");
        private const string BeastSortingLayerName = "UI Base Overlay";
        private const int BeastCreatureSortingOrder = 103;

        void Start() { SpawnAnimals(); }

        private void SpawnAnimals() {
            if (animalPrefabs == null || animalPrefabs.Length == 0) { return; }

            _animals = new AnimalState[animalCount];
            for (var i = 0; i < animalCount; i++) {
                var prefabIndex = Random.Range(0, animalPrefabs.Length);
                var prefab = animalPrefabs[prefabIndex];

                // Instantiate inactive to prevent CharacterController stepOffset
                // validation errors that fire during Awake before we can remove it.
                var wasActive = prefab.activeSelf;
                prefab.SetActive(false);
                var instance = Instantiate(prefab, transform);
                prefab.SetActive(wasActive);

                instance.name = $"Animal_{i}";

                // Strip third-party scripts and physics from prefabs (e.g. ithappy
                // CreatureMover/MovePlayerInput + CharacterController) that conflict
                // with AnimalEffect's own movement. Remove MonoBehaviours in reverse
                // component order so [RequireComponent] leaves are destroyed before
                // their dependencies (MovePlayerInput → CreatureMover → CharacterController).
                var monoBehaviours = instance.GetComponents<MonoBehaviour>();
                for (var j = monoBehaviours.Length - 1; j >= 0; j--) {
                    DestroyImmediate(monoBehaviours[j]);
                }
                foreach (var cc in instance.GetComponentsInChildren<CharacterController>()) {
                    DestroyImmediate(cc);
                }
                // Strip legacy Animation, Rigidbody, and Collider components that
                // some asset packs include (e.g. Africa Animals Pack).  These
                // conflict with AnimalEffect's Animator-driven, position-based
                // movement. Colliders must be destroyed before Rigidbody.
                foreach (var anim in instance.GetComponentsInChildren<Animation>()) {
                    DestroyImmediate(anim);
                }
                foreach (var col in instance.GetComponentsInChildren<Collider>()) {
                    DestroyImmediate(col);
                }
                foreach (var rb in instance.GetComponentsInChildren<Rigidbody>()) {
                    DestroyImmediate(rb);
                }

                // Configure animator while still inactive so it initializes
                // with the correct avatar and controller on activation.
                var animator = instance.GetComponent<Animator>();
                if (animator == null) { animator = instance.AddComponent<Animator>(); }
                if (animator.avatar == null) {
                    animator.avatar = AvatarBuilder.BuildGenericAvatar(instance, "");
                }
                if (animatorControllers != null && prefabIndex < animatorControllers.Length &&
                    animatorControllers[prefabIndex] != null) {
                    animator.runtimeAnimatorController = animatorControllers[prefabIndex];
                } else if (animatorController != null) {
                    animator.runtimeAnimatorController = animatorController;
                }
                animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;

                instance.SetActive(true);

                // Rebind after activation to ensure bone bindings are resolved
                if (animator != null) { animator.Rebind(); }

                instance.transform.localScale = new Vector3(animalScale, animalScale, animalScale);
                FaceCameraAtSpawn(instance.transform);

                var startPos = RandomWanderPoint();
                instance.transform.localPosition = startPos;

                foreach (var renderer in instance.GetComponentsInChildren<Renderer>()) {
                    renderer.sortingLayerName = BeastSortingLayerName;
                    renderer.sortingOrder = BeastCreatureSortingOrder;
                    BeastMaterialCompatibility.ConfigureRenderer(renderer);
                }
                foreach (var smr in instance.GetComponentsInChildren<SkinnedMeshRenderer>()) {
                    smr.updateWhenOffscreen = true;
                }
                if (clipToViewport) { RendererViewportClipper.Attach(instance); }

                _animals[i] = new AnimalState {
                    instance = instance,
                    animator = animator,
                    state = AnimalStateType.Idle,
                    targetPosition = startPos,
                    timer = Random.Range(minIdleDuration, maxIdleDuration)
                };
            }
        }

        private Vector3 RandomWanderPoint() {
            var angle = Random.Range(0f, Mathf.PI * 2f);
            var dist = Random.Range(0f, wanderRadius);
            return new Vector3(Mathf.Cos(angle) * dist, Mathf.Sin(angle) * dist, 0f);
        }

        void Update() {
            if (_animals == null) { return; }
            foreach (var animal in _animals) { UpdateAnimal(animal); }
        }

        private void UpdateAnimal(AnimalState animal) {
            if (animal.instance == null) { return; }

            // Recover if the animator transitions into the die state
            if (animal.animator != null && !animal.animator.IsInTransition(0)) {
                var stateInfo = animal.animator.GetCurrentAnimatorStateInfo(0);
                if (stateInfo.shortNameHash == DieHash) {
                    EnterIdle(animal);
                    return;
                }
            }

            switch (animal.state) {
                case AnimalStateType.Idle: UpdateIdle(animal); break;
                case AnimalStateType.Walking: UpdateWalking(animal); break;
                case AnimalStateType.Action: UpdateAction(animal); break;
            }
        }

        private void UpdateIdle(AnimalState animal) {
            animal.timer -= Time.deltaTime;
            if (animal.timer > 0f) { return; }

            animal.targetPosition = RandomWanderPoint();
            animal.state = AnimalStateType.Walking;
            if (HasState(animal.animator, WalkHash)) { animal.animator.Play(WalkHash); }

            UpdateFacing(animal);
        }

        private void UpdateWalking(AnimalState animal) {
            var pos = animal.instance.transform.localPosition;
            var step = moveSpeed * Time.deltaTime;
            animal.instance.transform.localPosition =
                    Vector3.MoveTowards(pos, animal.targetPosition, step);

            UpdateFacing(animal);

            if (Vector3.Distance(animal.instance.transform.localPosition, animal.targetPosition) <
                0.5f) {
                var actionHash = Random.value < 0.5f ? EatHash : AttackHash;
                if (Random.value < actionChance && HasState(animal.animator, actionHash)) {
                    animal.state = AnimalStateType.Action;
                    animal.timer = actionDuration;
                    animal.animator.Play(actionHash);
                } else {
                    EnterIdle(animal);
                }
            }
        }

        private void UpdateAction(AnimalState animal) {
            animal.timer -= Time.deltaTime;
            if (animal.timer <= 0f) { EnterIdle(animal); }
        }

        private void EnterIdle(AnimalState animal) {
            animal.state = AnimalStateType.Idle;
            animal.timer = Random.Range(minIdleDuration, maxIdleDuration);
            if (HasState(animal.animator, IdleHash)) { animal.animator.Play(IdleHash); }
        }

        private static bool HasState(Animator animator, int stateHash) {
            return animator != null && animator.HasState(0, stateHash);
        }

        private void FaceCameraAtSpawn(Transform instanceTransform) {
            var worldDirection = Camera.main != null ? instanceTransform.position -
                                                               Camera.main.transform.position
                                                     : transform.forward;
            var localDirection = transform.InverseTransformDirection(worldDirection);
            localDirection.y = 0f;
            if (localDirection.sqrMagnitude < 0.01f) { localDirection = Vector3.forward; }
            if (modelFrontFacesNegativeZ) { localDirection = -localDirection; }

            instanceTransform.localRotation = Quaternion.LookRotation(localDirection, Vector3.up);
        }

        private void UpdateFacing(AnimalState animal) {
            var direction = animal.targetPosition - animal.instance.transform.localPosition;
            if (direction.sqrMagnitude < 0.01f) { return; }

            // Smoothly rotate the 3D model on Y to face direction of travel in the XY plane
            var targetAngle = Mathf.Atan2(direction.x, direction.y) * Mathf.Rad2Deg;
            var targetRotation = Quaternion.Euler(0f, targetAngle, 0f);
            animal.instance.transform.localRotation = Quaternion.Slerp(
                    animal.instance.transform.localRotation,
                    targetRotation,
                    turnSpeed * Time.deltaTime);
        }

        [ContextMenu("Test: All Idle")]
        public void TestAllIdle() { ForEachAnimal(a => a.animator?.Play(IdleHash)); }

        [ContextMenu("Test: All Walk")]
        public void TestAllWalk() { ForEachAnimal(a => a.animator?.Play(WalkHash)); }

        [ContextMenu("Test: All Eat")]
        public void TestAllEat() { ForEachAnimal(a => a.animator?.Play(EatHash)); }

        [ContextMenu("Test: All Attack")]
        public void TestAllAttack() { ForEachAnimal(a => a.animator?.Play(AttackHash)); }

        private void ForEachAnimal(System.Action<AnimalState> action) {
            if (_animals == null) { return; }
            foreach (var animal in _animals) {
                if (animal.instance != null) {
                    animal.state = AnimalStateType.Idle;
                    animal.timer = 5f;
                    action(animal);
                }
            }
        }
    }
}
