using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;

namespace Player
{
    [RequireComponent(typeof(CharacterController))]
    public class PlayerController : MonoBehaviour
    {
        [Header("Movement Settings")]
        [SerializeField] private float walkSpeed = 5f;
        [SerializeField] private float crouchSpeed = 2.5f;
        [SerializeField] private float gravity = -9.81f;
    
        [Header("Look Settings")]
        public float mouseSensitivity = 2f;
        [SerializeField] private float lookXLimit = 80f;

        [Header("Crouch Settings")]
        [SerializeField] private float crouchHeight = 1f;
        [SerializeField] private float standHeight = 1.8f;
        [SerializeField] private float crouchTransitionSpeed = 10f;
        [SerializeField] Transform cameraRoot;

        [Header("Lean Settings (Q/E)")]
        [SerializeField] private float leanAngle = 15f;
        [SerializeField] private float leanOffset = 0.5f;
        [SerializeField] private float leanSpeed = 5f;

        [HideInInspector]
        public bool inputLocked = false;

        [Header("Audio Settings")]
        [SerializeField] private AudioSource footstepSource;
        
        [Tooltip("Расстояние между шагами при ходьбе (в метрах)")]
        [SerializeField] private float walkStepDistance = 2.0f; 
        
        [Tooltip("Расстояние между шагами при присяде (в метрах)")]
        [SerializeField] private float crouchStepDistance = 1.5f;
        [SerializeField] private AudioMixerGroup footstepMixerGroup; // NEW: Reference to the mixer group
        [SerializeField] private float walkStepInterval = 0.5f;
        [SerializeField] private float crouchStepInterval = 0.8f;
    
        [System.Serializable]
        public struct SurfaceLayer
        {
            public string surfaceName; 
            public AudioClip footstepSound; 
        }
    
        [SerializeField] private List<SurfaceLayer> surfaceLayers; 
        [SerializeField] private AudioClip defaultSound; 
    
        private float _distanceCovered;
        private CharacterController _characterController;
        private Vector3 _velocity;
        private Vector2 _rotation = Vector2.zero;
        private float _currentHeight;
        private Vector3 _lastPosition;
    
        // Стейты
        private bool _isCrouching;
    
    
        // Ссылка на активный триггер скрипа
        private CreakSoundTrigger _activeCreakTrigger;
        private float _currentLean;

        private void Awake()
        {
            _characterController = GetComponent<CharacterController>();
        
            if (footstepSource == null) 
                footstepSource = GetComponent<AudioSource>();

            // NEW: Assign the mixer group to the AudioSource
            if (footstepSource != null && footstepMixerGroup != null)
            {
                footstepSource.outputAudioMixerGroup = footstepMixerGroup;
            }
            else
            {
                Debug.LogError("Footstep AudioSource or MixerGroup is not assigned!", this);
            }
        }

        void Start()
        {
            _characterController = GetComponent<CharacterController>();
            _currentHeight = standHeight;
            _lastPosition = transform.position;
        
            // Скрываем курсор
            Cursor.lockState = CursorLockMode.Locked;
            Cursor.visible = false;
        }

        void Update()
        {
            if (inputLocked)
            {
                HandleLean(true);
                return;
            }

            HandleMovement();
            HandleMouseLook();
            HandleCrouch();
            HandleLean(false);
            HandleFootsteps();
        }

        private void HandleMovement()
        {
            // Определяем, на земле ли мы
            bool isGrounded = _characterController.isGrounded;
            if (isGrounded && _velocity.y < 0)
            {
                _velocity.y = -2f;
            }

            // Ввод WASD
            float moveX = Input.GetAxis("Horizontal");
            float moveZ = Input.GetAxis("Vertical");

            // Выбор скорости (идем или крадемся)
            float speed = _isCrouching ? crouchSpeed : walkSpeed;

            // Вектор движения относительно направления взгляда
            Vector3 move = transform.right * moveX + transform.forward * moveZ;
        
            // Применяем движение
            _characterController.Move(move * (speed * Time.deltaTime));

            // Применяем гравитацию
            _velocity.y += gravity * Time.deltaTime;
            _characterController.Move(_velocity * Time.deltaTime);
        }

        private void HandleMouseLook()
        {
            _rotation.y += Input.GetAxis("Mouse X") * mouseSensitivity;
            _rotation.x += -Input.GetAxis("Mouse Y") * mouseSensitivity;
        
            // Ограничение взгляда вверх/вниз
            _rotation.x = Mathf.Clamp(_rotation.x, -lookXLimit, lookXLimit);

            // Вращаем тело персонажа по Y (влево-вправо)
            transform.localRotation = Quaternion.Euler(0, _rotation.y, 0);
        
            // Вращаем камеру по X (вверх-вниз). 
            // ВАЖНО: Вращаем CameraRoot, а не саму камеру, чтобы не ломать наклоны
            cameraRoot.localRotation = Quaternion.Euler(_rotation.x, 0, 0);
        }

        private void HandleCrouch()
        {
            // Левый Ctrl
            if (Input.GetKeyDown(KeyCode.LeftControl)) _isCrouching = true;
            if (Input.GetKeyUp(KeyCode.LeftControl)) _isCrouching = false;

            // Вычисляем целевую высоту
            float targetHeight = _isCrouching ? crouchHeight : standHeight;
        
            // Плавное изменение высоты контроллера (чтобы не проваливаться сквозь пол, меняем center)
            // Но проще менять высоту контроллера и позицию камеры
            _currentHeight = Mathf.Lerp(_currentHeight, targetHeight, Time.deltaTime * crouchTransitionSpeed);
            _characterController.height = _currentHeight;
        
            // Корректируем центр контроллера, чтобы ноги оставались на земле
            _characterController.center = new Vector3(0, _currentHeight / 2, 0);
        
            // Корректируем высоту камеры (глаз)
            // Когда стоим - камера высоко, когда сидим - ниже.
            // Предполагаем, что глаза находятся чуть ниже макушки
            float targetCamY = _currentHeight - 0.2f; 
            Vector3 camPos = cameraRoot.localPosition;
            camPos.y = targetCamY;
        
            // Тут интерполяцию можно не делать, так как height интерполируется выше, но для подстраховки:
            cameraRoot.localPosition = camPos; 
        }

        private void HandleLean(bool forceReset)
        {
            float targetLean = 0f;

            if (!forceReset)
            {
                if (Input.GetKey(KeyCode.Q)) targetLean = 1f;
                else if (Input.GetKey(KeyCode.E)) targetLean = -1f;
            }

            // Плавный переход к цели
            _currentLean = Mathf.MoveTowards(_currentLean, targetLean, leanSpeed * Time.deltaTime);
            Quaternion lookRotation = Quaternion.Euler(_rotation.x, 0, 0);
            Quaternion leanRotation = Quaternion.Euler(0, 0, _currentLean * leanAngle);
            cameraRoot.localRotation = lookRotation * leanRotation;

            // Рассчитываем наклон и смещение
            // Вращение по Z (Roll)
            // Нам нужно применить наклон поверх взгляда вверх-вниз.
            // Мы знаем, что в HandleMouseLook мы задаем (rotation.x, 0, 0).
            // Добавим к этому Z поворот.
            cameraRoot.localRotation = Quaternion.Euler(_rotation.x, 0, _currentLean * leanAngle);

            // Смещение позиции (чтобы голова реально сдвигалась, а не просто крутилась)
            // Сдвигаем дочернюю камеру, а не рут, или сам рут локально
            // Лучше сдвигать CameraRoot локально по X
            Vector3 currentPos = cameraRoot.localPosition;
            float targetX = -_currentLean * leanOffset;
            currentPos.x = targetX;
            cameraRoot.localPosition = currentPos;
        }

        private void HandleFootsteps()
        {
            if (!_characterController.isGrounded) return;

            //  перемещение по горизонтали 
            Vector3 currentPosFlat = new Vector3(transform.position.x, 0, transform.position.z);
            Vector3 lastPosFlat = new Vector3(_lastPosition.x, 0, _lastPosition.z);
            float distanceMoved = Vector3.Distance(currentPosFlat, lastPosFlat);
        
            _lastPosition = transform.position;
         
            // Если мы вообще двигались, добавляем пройденное расстояние к счетчику
            if (distanceMoved > 0)
            {
                _distanceCovered += distanceMoved;
            }

            float currentInterval = _isCrouching ? crouchStepInterval : walkStepInterval;
            var _stepTimer = Time.deltaTime;
            // Определяем, какое расстояние нужно пройти для следующего шага
            float stepDistance = _isCrouching ? crouchStepDistance : walkStepDistance;

            // Если накопленное расстояние превышает дистанцию шага
            if (_distanceCovered >= stepDistance)
            {
                PlayFootstepSound();
                _distanceCovered -= stepDistance; // Вычитаем дистанцию шага, а не сбрасываем в 0
            }
        }

        private void PlayFootstepSound()
        {
            if (_activeCreakTrigger != null && _activeCreakTrigger.CreakSound != null)
            {
                PlaySound(_activeCreakTrigger.CreakSound);
                return; // Выходим, чтобы не проигрывать обычный звук шага
            }
        
            RaycastHit hit;
         
            if (Physics.Raycast(transform.position + Vector3.up * 0.5f, Vector3.down, out hit, 2.0f))
            {
                string keyName = "";

                Terrain terrain = hit.collider.GetComponent<Terrain>();
                if (terrain != null)
                {
                    keyName = GetDominantTextureName(transform.position, terrain);
                }
                else
                {
                    keyName = hit.collider.tag;
                }

                // Ищем звук
                SurfaceLayer foundLayer = surfaceLayers.Find(layer => layer.surfaceName == keyName);

                if (foundLayer.footstepSound != null)
                {
                    PlaySound(foundLayer.footstepSound);
                }
                else
                {
                    if (defaultSound != null) PlaySound(defaultSound);
                }
            }
        }
    
        private string GetDominantTextureName(Vector3 playerPos, Terrain terrain)
        {
            TerrainData terrainData = terrain.terrainData;
            Vector3 terrainPos = terrain.transform.position;

            // 1. Переводим координаты игрока в координаты карты текстур (Alphamap)
            int mapX = (int)(((playerPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
            int mapZ = (int)(((playerPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);

            // 2. Получаем смешивание текстур в этой точке (3-мерный массив [z, x, layer])
            float[,,] splatmapData = terrainData.GetAlphamaps(mapX, mapZ, 1, 1);

            // 3. Ищем индекс самой "сильной" текстуры
            float maxMix = 0;
            int maxIndex = 0;

            // Проходимся по всем слоям текстур
            for (int i = 0; i < terrainData.alphamapLayers; i++)
            {
                if (splatmapData[0, 0, i] > maxMix)
                {
                    maxMix = splatmapData[0, 0, i];
                    maxIndex = i;
                }
            }

            // 4. Возвращаем имя слоя (Terrain Layer Name)
            return terrainData.terrainLayers[maxIndex].name;
        }

        private void PlaySound(AudioClip clip)
        {
            if (clip == null) return;

            footstepSource.pitch = Random.Range(0.95f, 1.05f);
            footstepSource.volume = Random.Range(0.9f, 1.0f);

            footstepSource.PlayOneShot(clip);
        }
    
        private void OnTriggerEnter(Collider other)
        {
            if (other.TryGetComponent<CreakSoundTrigger>(out var creakTrigger))
            {
                _activeCreakTrigger = creakTrigger;
            }
        }

        private void OnTriggerExit(Collider other)
        {
            if (other.TryGetComponent<CreakSoundTrigger>(out var creakTrigger) && _activeCreakTrigger == creakTrigger)
            {
                _activeCreakTrigger = null;
            }
        }
    
        public void PlayOneShotSound(AudioClip clip)
        {
            if (clip != null) footstepSource.PlayOneShot(clip);
        }
    }
}