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

namespace Shooter.Gameplay
{
    /// <summary>
    /// Персонаж игрока: движение, автоматическое прицеливание по ближайшей цели,
    /// стрельба активным оружием, бросок гранаты, щит и обработка смерти. Ввод
    /// читается из <see cref="PlayerControl"/>, здоровье — из <see cref="DamageControl"/>.
    /// </summary>
    public class PlayerChar : MonoBehaviour
    {
        public static PlayerChar m_Current;

        [Header("Узлы модели")]
        [SerializeField] private Transform m_TurnBase; // корпус, разворачивается по движению
        [SerializeField] private Transform m_AimBase;  // узел прицела, доворачивается к цели
        public Transform[] m_WeaponHands;              // руки с оружием (для отдачи)
        public Transform m_FirePoint;

        [Header("Движение")]
        [Tooltip("Ускорение от ввода")]
        public float m_MoveAccel = 5f;
        [Tooltip("Максимальная горизонтальная скорость")]
        public float m_MaxSpeed = 11f;
        [Tooltip("Торможение, когда ввода нет (доля скорости за кадр физики)")]
        public float m_GroundDrag = 0.4f;
        [Tooltip("Скорость доворота корпуса по направлению движения")]
        public float m_TurnLerpSpeed = 10f;

        [Header("Автоприцел")]
        [Tooltip("Максимальный угол до цели, в пределах которого она берётся в прицел")]
        public float m_AimMaxAngle = 40f;
        [Tooltip("Максимальная дальность захвата цели")]
        public float m_AimMaxDistance = 30f;
        [Tooltip("Скорость доворота прицела к цели")]
        public float m_AimLerpSpeed = 20f;

        [Header("Оружие и способности")]
        public Weapon_Base[] m_Weapons;
        [Tooltip("Сколько секунд держится усиление оружия")]
        public float m_WeaponPowerDuration = 16f;
        public GameObject m_WeaponPowerParticle;
        public GameObject m_GrenadePrefab1;

        [Header("Эффекты и ссылки")]
        public GameObject m_DeathParticle;
        public GameObject m_HitParticlePrefab;
        public GameObject m_DashParticle;
        public AnimationCurve m_DashCurve;
        public Animator m_Animator;
        public GameObject m_ShieldObject;

        // --- Состояние во время игры ---
        [HideInInspector] public DamageControl m_DamageControl;
        [HideInInspector] public CameraControl m_MyCamera;
        [HideInInspector] public PlayerPowers m_PlayerPowers;
        [HideInInspector] public bool m_InControl = false;
        [HideInInspector] public bool m_CanShoot = false;
        [HideInInspector] public int m_CashAmount = 0;
        [HideInInspector] public int m_ResourceAmount = 0;
        [HideInInspector] public bool m_IsDead = false;
        [HideInInspector] public int m_WeaponNum = 0;
        public bool m_HaveKey = false;
        public TargetObject m_TempTarget;   // цель, на которую сейчас наведён прицел
        public TargetObject m_LockedTarget; // зафиксированная цель (для опциональной фичи захвата)
        [HideInInspector] public int m_WpnPowerLevel = 0;
        [HideInInspector] public float m_WpnPowerTime = 0;

        private Rigidbody m_Rigidbody;
        private Vector3 m_MovementInput;
        private Vector3 m_DashDirection;
        private bool m_Input_Fire;

        void Awake()
        {
            m_Current = this;
            m_Rigidbody = GetComponent<Rigidbody>();
            m_PlayerPowers = GetComponent<PlayerPowers>();
        }

        void Start()
        {
            m_DamageControl = GetComponent<DamageControl>();
            m_DamageControl.OnDamaged.AddListener(HandleDamage);
            m_InControl = true;

            m_CashAmount = 0;
            m_ResourceAmount = 0;

            // Щит отвязываем от игрока, чтобы он не наследовал повороты корпуса.
            m_ShieldObject.transform.SetParent(null);
            m_WeaponPowerParticle.SetActive(false);
        }

        void Update()
        {
            m_Input_Fire = false;

            if (m_InControl)
            {
                m_Input_Fire = PlayerControl.MainPlayerController.Input_FireHold;

                // Z — применить активную способность (пока только граната).
                if (Input.GetKeyDown(KeyCode.Z) && m_PlayerPowers.m_HavePower)
                {
                    switch (m_PlayerPowers.m_PowerNum)
                    {
                        case 0:
                            if (m_PlayerPowers.m_AmmoCount > 0)
                            {
                                ThrowGrenade();
                                m_PlayerPowers.m_AmmoCount--;
                            }
                            break;
                    }
                }

                #region Опциональные действия игрока (Глава 4) — раскомментируйте, чтобы включить
                //if (Input.GetMouseButton(1)) { /* альтернативный огонь */ }
                //if (Input.GetKeyDown(KeyCode.C)) CheckMelleeAttack();    // ближний бой
                //if (Input.GetKeyDown(KeyCode.V)) SetWeaponPowerLevel(1);  // принудительное усиление
                //if (Input.GetMouseButtonDown(1)) ThrowGrenade();          // граната по правой кнопке
                //if (Input.GetMouseButtonDown(1)) StartDash();             // рывок по правой кнопке
                #endregion

                m_MovementInput = PlayerControl.MainPlayerController.m_Input_Movement;

                // Выбираем ближайшую к взгляду цель и доворачиваем к ней прицел.
                TargetObject bestTarget = FindBestTarget();
                if (bestTarget != null)
                {
                    Vector3 targetDir = bestTarget.m_TargetCenter.position - m_FirePoint.position;
                    targetDir.y = 0;
                    m_AimBase.rotation = Quaternion.Lerp(m_AimBase.rotation, Quaternion.LookRotation(targetDir), m_AimLerpSpeed * Time.deltaTime);
                    m_TempTarget = bestTarget;
                }
                else
                {
                    m_TempTarget = null;
                    m_AimBase.localRotation = Quaternion.Lerp(m_AimBase.localRotation, Quaternion.identity, m_AimLerpSpeed * Time.deltaTime);
                }

                #region Опциональная фича: фиксация цели (Глава 4)
                //if (m_Input_LockAim)
                //{
                //    if (m_LockedTarget == null) m_LockedTarget = bestTarget;
                //    if (m_LockedTarget != null)
                //    {
                //        Vector3 faceDirection = m_LockedTarget.transform.position - transform.position;
                //        faceDirection.y = 0;
                //        faceDirection.Normalize();
                //        m_AimBase.rotation = Quaternion.Lerp(m_AimBase.rotation, Quaternion.LookRotation(faceDirection), m_AimLerpSpeed * Time.deltaTime);
                //    }
                //}
                #endregion

                // Корпус разворачивается по направлению движения.
                if (m_MovementInput != Vector3.zero)
                {
                    Vector3 faceDirection = m_MovementInput;
                    faceDirection.y = 0;
                    faceDirection.Normalize();
                    m_TurnBase.rotation = Quaternion.Lerp(m_TurnBase.rotation, Quaternion.LookRotation(faceDirection), m_TurnLerpSpeed * Time.deltaTime);
                }

                m_Weapons[m_WeaponNum].Input_FireHold = m_Input_Fire;
            }

            // Усиление оружия истекает по таймеру.
            if (m_WpnPowerLevel == 1)
            {
                m_WpnPowerTime -= Time.deltaTime;
                if (m_WpnPowerTime <= 0)
                {
                    m_WeaponPowerParticle.SetActive(false);
                    SetWeaponPowerLevel(0);
                }
            }

            // Параметр анимации бега по горизонтальной скорости.
            Vector3 vSpeed = m_Rigidbody.linearVelocity;
            vSpeed.y = 0;
            float runSpeed = Mathf.Clamp(vSpeed.magnitude / 10f, 0, 1);
            m_Animator.SetFloat("RunSpeed", runSpeed);

            // Щит висит над игроком (он отвязан от корпуса).
            m_ShieldObject.transform.position = transform.position + new Vector3(0, 1, 0);

            // Смерть по нулю здоровья.
            if (!m_IsDead && m_DamageControl.Damage <= 0)
            {
                m_IsDead = true;
                GameObject obj = Instantiate(m_DeathParticle);
                obj.transform.position = transform.position + new Vector3(0, 1, 0);
                Destroy(obj, 3);
                gameObject.SetActive(false);
            }
        }

        /// <summary>
        /// Ищет лучшую цель для автоприцела: из зарегистрированных в
        /// <see cref="TargetsControl"/> берёт ту, что ближе всего к направлению взгляда
        /// корпуса, в пределах <see cref="m_AimMaxAngle"/> и <see cref="m_AimMaxDistance"/>.
        /// </summary>
        TargetObject FindBestTarget()
        {
            List<TargetObject> targets = TargetsControl.m_Main.m_Targets;

            TargetObject bestTarget = null;
            float minAngle = m_AimMaxAngle;
            foreach (TargetObject target in targets)
            {
                if (target == null)
                    continue;

                Vector3 dir = target.m_TargetCenter.position - transform.position;
                dir.y = 0;
                if (dir.magnitude > m_AimMaxDistance)
                    continue;

                float delta = Vector3.Angle(m_TurnBase.forward, dir);
                if (delta < minAngle)
                {
                    bestTarget = target;
                    minAngle = delta;
                }
            }
            return bestTarget;
        }

        void FixedUpdate()
        {
            Vector3 totalVelocity = m_Rigidbody.linearVelocity;
            if (m_MovementInput != Vector3.zero)
            {
                totalVelocity += m_MoveAccel * m_MovementInput;
                totalVelocity.y = 0;
                totalVelocity = Vector3.ClampMagnitude(totalVelocity, m_MaxSpeed);
                totalVelocity.y = m_Rigidbody.linearVelocity.y;
                m_Rigidbody.linearVelocity = totalVelocity;
            }
            else
            {
                totalVelocity -= m_GroundDrag * totalVelocity;
                totalVelocity.y = m_Rigidbody.linearVelocity.y;
                m_Rigidbody.linearVelocity = totalVelocity;
            }
        }

        public void HandleDamage()
        {
            CameraControl.m_Current.StartShake(.2f, .1f);
        }

        void LateUpdate()
        {
            // Отдача: отводим руки с оружием по текущему импульсу выстрела.
            float recoil = m_Weapons[m_WeaponNum].RecoilTimer;
            m_WeaponHands[0].localRotation *= Quaternion.Euler(0, -4 * recoil, 0);
            m_WeaponHands[1].localRotation *= Quaternion.Euler(0, -4 * recoil, 0);
            m_WeaponHands[0].localPosition += new Vector3(0, 0, -.5f * recoil);
        }

        public void AddAmmo(int count)
        {
            // Зарезервировано: пополнение патронов (вызов из анимаций/пикапов).
        }

        public void SetWeaponPowerLevel(int level)
        {
            m_WpnPowerLevel = level;
            if (level == 1)
            {
                m_WpnPowerTime = m_WeaponPowerDuration;
                m_WeaponPowerParticle.SetActive(true);
            }

            foreach (Weapon_Base w in m_Weapons)
                w.m_PowerLevel = level;
        }

        public void SetWeapon(int num)
        {
            foreach (Weapon_Base w in m_Weapons)
                w.Input_FireHold = false;
            m_WeaponNum = num;
        }

        public void ThrowGrenade()
        {
            Vector3 start = transform.position;
            Vector3 end = PlayerControl.MainPlayerController.AimPosition + new Vector3(0, 1, 0);
            GameObject obj = Instantiate(m_GrenadePrefab1);
            obj.transform.position = transform.position;
            PlayerGrenade g = obj.GetComponent<PlayerGrenade>();
            g.m_StartPosition = start;
            g.m_TargetPosition = end;
        }

        public void HandlePickup(string itemType, int count)
        {
            if (itemType == "Gem")
                PlayerControl.MainPlayerController.m_GemCount++;
            else if (itemType == "WeaponPower")
                SetWeaponPowerLevel(1);
            else if (itemType == "Weapon_Pistol")
                SetWeapon(0);
            else if (itemType == "Weapon_Shotgun")
                SetWeapon(1);
            else if (itemType == "Power_Grenade")
                m_PlayerPowers.SetNewPower(0);
            else if (itemType == "Health")
                m_DamageControl.AddHealth(count);
            else if (itemType == "Key")
                m_HaveKey = true;
        }

        #region Опциональная фича: ближний бой (Глава 4)
        /// <summary>
        /// Удар по площади перед игроком: бьёт врагов и разрушаемые блоки в радиусе.
        /// В активной сборке не вызывается — включается раскомментированием в Update.
        /// </summary>
        public void CheckMelleeAttack()
        {
            Collider[] colls = Physics.OverlapSphere(transform.position + 2 * m_AimBase.forward, 1);
            foreach (Collider col in colls)
            {
                if (col.gameObject.tag == "Enemy")
                {
                    DamageControl d = col.gameObject.GetComponent<DamageControl>();
                    if (d != null)
                    {
                        Vector3 dir = col.gameObject.transform.position - transform.position;
                        dir.Normalize();
                        d.ApplyDamage(5, dir, 1);
                    }
                }
                else if (col.gameObject.tag == "Block")
                {
                    DamageControl d = col.gameObject.GetComponent<DamageControl>();
                    if (d != null)
                        d.ApplyDamage(1, transform.forward, 1);
                }
            }
        }
        #endregion

        #region Опциональная фича: рывок-dash (Глава 4)
        public void StartDash()
        {
            m_DashDirection = m_MovementInput;
            if (m_DashDirection != Vector3.zero)
                StartCoroutine(Co_Dash());
        }

        IEnumerator Co_Dash()
        {
            GameObject obj = Instantiate(m_DashParticle);
            obj.transform.position = transform.position + new Vector3(0, 1, 0);
            obj.transform.forward = m_DashDirection;
            Destroy(obj, 3);

            Collider collider = GetComponent<Collider>();
            collider.enabled = false;
            m_Animator.SetBool("Dashing", true);
            m_Rigidbody.isKinematic = true;

            float lerp = 0;
            Vector3 startPos = transform.position;
            Vector3 endPos = transform.position + 6 * m_DashDirection;
            while (lerp < 1)
            {
                transform.position = Vector3.Lerp(startPos, endPos, m_DashCurve.Evaluate(lerp));
                lerp += 4 * Time.deltaTime;
                yield return null;
            }

            transform.position = endPos;
            m_Rigidbody.isKinematic = false;
            m_DashDirection = Vector3.zero;
            m_Animator.SetBool("Dashing", false);
            collider.enabled = true;
        }
        #endregion

        public void Hit()
        {
            // Зарезервировано под реакцию на попадание (эффект/тряска).
        }
    }
}
