using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using TMPro;

public class CharacterMovement : MonoBehaviour
{
    //jump system variables
    private bool jump;
    GroundCheck groundCheck;
    public FallCheck fallCheck;
    [SerializeField] private float jumpForce = 20f;
    [SerializeField] private float gravity = -9.81f;
    [SerializeField] private float gravityScale = 5f;
    private float floatSpeed;
    private float gvelocity;

    //camera direction variables
    private bool useCharacterForward = false;
    private bool lockToCameraForward = false;

    [SerializeField] private float turnSpeed = 10f;

    public GameObject player;
    public GameObject enemy;

    public float distance;
    private AnimatorClipInfo[] m_CurrentClipInfo;
    private float m_CurrentClipLength;
    private string m_ClipName;
    public float nextAnimationTime;
    public float animationInterval;

    private float turnSpeedMultiplier;

    private float direction = 0f;
    private bool isSprinting = false;

    public Animator anim;

    private Vector3 targetDirection;

    private Quaternion freeRotation;
    private Camera mainCamera;
    private float velocity;

    // Cached reference: avoids FindFirstObjectByType every FixedUpdate (was 300 calls/sec)
    private PlayerManagerNew _cachedPlayerManager;
    private bool _playerManagerCached;
    
    public bool block;

    Vector2 input;
    
    public PlayerScript playerScript;
    
    // New modular input handler (used if PlayerScript is not present)
    private CharacterInputHandler inputHandler;

    private AnimatorClipInfo[] playerclipinfo;

    private Vector3 playerDirection;
    private float rotationSpeed;

    [SerializeField] private float joystickSensitivity = 1f;
    private Vector2 joystickInput;
    float inputThreshold;

    // Scale protection: some animation clips contain baked scale curves from FBX import
    // that can shrink the character model. We save the original scale and restore it every frame.
    private Vector3 originalScale;

    [Header("Grounding")]
    [Tooltip("Layers to raycast against for ground detection. Must exclude character layers to prevent floating.")]
    [SerializeField] private LayerMask groundLayerMask = 0;

    // Animation transition parameters
    private float animationBlendTime = 0.15f; // Time to blend between animations
    private float movementSmoothTime = 0.1f; // Time to smooth movement speed change
    private float rotationSmoothTime = 0.1f; // Time to smooth rotation
    private float currentMoveSpeed = 0f; // Current movement speed for smooth transitions
    private float currentTurnSpeed = 0f; // Current turn speed for smooth transitions

    [SerializeField] private float smoothTurnSpeed = 5f; // Lower = smoother, for realistic turning

    // Walk/Run sound effects
    [Header("Footstep Sound Effects")]
    public AudioClip walkSoundEffect;
    public AudioClip runSoundEffect;
    private AudioSource footstepAudioSource;
    private bool isFootstepPlaying = false;
    private float lastSpeedParam = 0f;

    //Link to Mission Handler Script
    //Initialize the scripts
    MissionHandler mission_handler;
    GameStatsManager game_statsManager;
    TextMeshProUGUI txt;

    // Add reference to AnimationManager
    private AnimationManager animationManager;

    void OnEnable(){
        GameObject gamestatsmanager_go = GameObject.Find("GameStatsManager");
        if (gamestatsmanager_go != null) {
            game_statsManager = gamestatsmanager_go.GetComponent<GameStatsManager>();
            mission_handler = gamestatsmanager_go.GetComponent<MissionHandler>();
        } else {
            DevLog.LogWarning("GameStatsManager not found - mission tracking will be disabled");
        }

        //txt = gamestatsmanager_go.transform.Find("Canvas/move_txt").GetComponent<TextMeshProUGUI>();

        if (mission_handler != null){
            //DevLog.LogWarning("Character Movement Found");
        }
    }


    // Start is called before the first frame update
    void Start()
    {
        // Save original scale before anything can modify it
        originalScale = transform.localScale;

        jumpForce = 40;
        gravity = -9.81f;
        gravityScale = 5;
        rotationSpeed = 2;
        input = Vector2.zero;
        anim = GetComponent<Animator>();
        
        // Set animator transition parameters
        if (anim != null)
        {
            foreach (var layer in new int[] { 0, 1, 2 }) // Check multiple layers if needed
            {
                if (layer < anim.layerCount)
                {
                    for (int i = 0; i < anim.parameters.Length; i++)
                    {
                        // Adjust transition times in the Animator
                        AnimatorControllerParameter param = anim.parameters[i];
                        if (param.type == AnimatorControllerParameterType.Float && param.name == "TransitionTime")
                        {
                            anim.SetFloat(param.name, animationBlendTime);
                        }
                    }
                }
            }
        }
        
        mainCamera = Camera.main;
        player = GameObject.FindGameObjectWithTag("Player");
        enemy = GameObject.FindGameObjectWithTag("Enemy");
        animationInterval = 3.0f;
        block = false;
        
        // Get PlayerScript from this character - this is essential for movement
        // Try new modular CharacterInputHandler first, fall back to PlayerScript
        inputHandler = GetComponent<CharacterInputHandler>();
        playerScript = GetComponent<PlayerScript>();
        
        if (inputHandler == null && playerScript == null)
        {
            DevLog.LogWarning($"[CharacterMovement] No CharacterInputHandler or PlayerScript found on {gameObject.name}. Movement will not work for player-controlled characters.");
        }
        else if (inputHandler != null)
        {
            DevLog.Log($"[CharacterMovement] Using new modular CharacterInputHandler on {gameObject.name}");
        }
        else
        {
            DevLog.Log($"[CharacterMovement] Using legacy PlayerScript on {gameObject.name}");
        }
        

        
        // Get or add required components with null checks
        groundCheck = GetComponent<GroundCheck>();
        if (groundCheck == null) {
            groundCheck = gameObject.AddComponent<GroundCheck>();
            DevLog.LogWarning("GroundCheck component not found, adding one automatically.");
        }
        
        fallCheck = GetComponent<FallCheck>();
        if (fallCheck == null) {
            fallCheck = gameObject.AddComponent<FallCheck>();
            DevLog.LogWarning("FallCheck component not found, adding one automatically.");
        }
        
        inputThreshold = 0.3f;
        
        // Get or add AnimationManager component
        animationManager = GetComponent<AnimationManager>();
        if (animationManager == null)
        {
            animationManager = gameObject.AddComponent<AnimationManager>();
        }

        // Setup footstep audio source
        footstepAudioSource = GetComponent<AudioSource>();
        if (footstepAudioSource == null)
        {
            footstepAudioSource = gameObject.AddComponent<AudioSource>();
        }
        footstepAudioSource.spatialBlend = 1f; // 3D sound
        footstepAudioSource.loop = true;
        footstepAudioSource.playOnAwake = false;
    }

    void FixedUpdate()
    {
        // Cached PlayerManagerNew lookup — avoids FindFirstObjectByType every FixedUpdate
        if (!_playerManagerCached)
        {
            _cachedPlayerManager = GameObject.FindFirstObjectByType<PlayerManagerNew>();
            _playerManagerCached = true;
        }
        if (_cachedPlayerManager != null && _cachedPlayerManager.pause)
            return;
        
        // Determine if movement should be blocked based on available input source
        bool isBlocking = false;
        bool isDodging = false;
        bool hasInputSource = false;
        
        if (inputHandler != null)
        {
            isBlocking = inputHandler.block;
            isDodging = inputHandler.dodge;
            hasInputSource = true;
        }
        else if (playerScript != null)
        {
            isBlocking = playerScript.block;
            isDodging = playerScript.dodge;
            hasInputSource = true;
        }
        
        // Always update footstep sounds based on animator state (for AI and all characters)
        UpdateFootstepSoundsFromAnimator();
        
        // No input source - don't process player movement
        // This is fine for AI characters which use NavMeshAgent
        if (!hasInputSource)
        {
            return;
        }
        
        // Block movement only during blocking or dodging
        if (isBlocking || isDodging)
            return;
            
        gvelocity += gravity * gravityScale * Time.deltaTime;
        
        if (groundCheck != null && groundCheck.isGrounded && gvelocity < 0)
        {
            //print("jump reset");
            gvelocity = 0;
            jump = false;
        }
        
        // Get input from available source (prefer new modular handler)
        if (inputHandler != null) {
            input = inputHandler.input;
        } else if (playerScript != null) {
            input = playerScript.input;
        }
        inputThreshold = 0.3f; // Adjust this value as needed
        
        //ensure the character always stays upright
        if (transform.rotation.z > 0.5 || transform.rotation.x > 0.5 || transform.rotation.z < -0.5 || transform.rotation.x < -0.5)
        {
            print("make player upright");
            Quaternion rotation = Quaternion.Euler(0, transform.rotation.y, 0);
            //transform.rotation = rotation;

            // Dampen towards the target rotation
            transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime);
        }
        //ensure that the character is always grounded if they are not jumping
        if (jump == false)
        {
            // Auto-configure ground layer mask if not set (exclude character layers)
            if (groundLayerMask.value == 0)
            {
                groundLayerMask = ~0; // Start with everything
                int playerLayer = LayerMask.NameToLayer("Player");
                int enemyLayer = LayerMask.NameToLayer("Enemy");
                int characterLayer = LayerMask.NameToLayer("Character");
                int ignoreRaycast = LayerMask.NameToLayer("Ignore Raycast");
                if (playerLayer >= 0) groundLayerMask &= ~(1 << playerLayer);
                if (enemyLayer >= 0) groundLayerMask &= ~(1 << enemyLayer);
                if (characterLayer >= 0) groundLayerMask &= ~(1 << characterLayer);
                if (ignoreRaycast >= 0) groundLayerMask &= ~(1 << ignoreRaycast);
            }

            RaycastHit hit;
            // CRITICAL: Use groundLayerMask to ignore character body colliders
            // Without this, raycast hits character chest/head colliders and thinks "ground" is at chest height
            if (Physics.Raycast(transform.position + Vector3.up * 0.5f, Vector3.down, out hit, 50f, groundLayerMask))
            {
                float groundY = hit.point.y;
                if (transform.position.y > groundY + 0.15f)
                {
                    // Instant snap to ground instead of slow 1f/frame drift
                    transform.position = new Vector3(transform.position.x, groundY + 0.05f, transform.position.z);
                }
            }
        }

        // Protect against animation clips overwriting scale
        if (transform.localScale != originalScale)
        {
            transform.localScale = originalScale;
        }

        Move();
    }

    public virtual void UpdateTargetDirection()
    {
        if (playerScript != null) {
            input = playerScript.input;
        }

        if (mainCamera == null)
            mainCamera = Camera.main;

        Vector3 forward = mainCamera.transform.forward;
        Vector3 right = mainCamera.transform.right;

        // Project onto XZ plane
        forward.y = 0f;
        right.y = 0f;
        forward.Normalize();
        right.Normalize();

        // input.x = horizontal (A/D or left/right), input.y = vertical (W/S or up/down)
        targetDirection = right * input.x + forward * input.y;
    }

   public void Move()
    {
        if (IsInHitReactionAnimation())//
            return;

        UpdateTargetDirection();

        // Calculate raw input magnitude for animation
        float inputMagnitude = input.magnitude;

        // Define thresholds
        float walkThreshold = 0.1f; // Minimum input to start walking
        float runThreshold = 0.7f;  // Input magnitude to start running

        float speedParam = 0f;

        if (inputMagnitude > walkThreshold && inputMagnitude < runThreshold)
        {
            // Walking
            speedParam = 0.2f; // Match your blend tree's walk value
        }
        else if (inputMagnitude >= runThreshold)
        {
            // Running
            speedParam = 1f;
        }
        else
        {
            // Idle
            speedParam = 0f;
        }

        // Debug logging for movement diagnosis
        if (inputMagnitude > 0.01f)
        {
            DevLog.Log($"[CharacterMovement] Input: ({input.x:F2}, {input.y:F2}), Magnitude: {inputMagnitude:F2}, Speed: {speedParam}");
        }

        anim.SetFloat("Speed", speedParam);

        // --- Footstep Sound Logic ---
        if (speedParam == 0f)
        {
            if (footstepAudioSource.isPlaying)
                footstepAudioSource.Stop();
            isFootstepPlaying = false;
        }
        else if (speedParam > 0f && !isFootstepPlaying)
        {
            if (speedParam < 0.7f && walkSoundEffect != null)
            {
                footstepAudioSource.clip = walkSoundEffect;
                footstepAudioSource.pitch = 1f;
                footstepAudioSource.Play();
                isFootstepPlaying = true;
            }
            else if (speedParam >= 0.7f && runSoundEffect != null)
            {
                footstepAudioSource.clip = runSoundEffect;
                footstepAudioSource.pitch = 1.1f;
                footstepAudioSource.Play();
                isFootstepPlaying = true;
            }
        }
        else if (isFootstepPlaying)
        {
            // Switch between walk/run sound if needed
            if (speedParam < 0.7f && footstepAudioSource.clip != walkSoundEffect && walkSoundEffect != null)
            {
                footstepAudioSource.clip = walkSoundEffect;
                footstepAudioSource.pitch = 1f;
                footstepAudioSource.Play();
            }
            else if (speedParam >= 0.7f && footstepAudioSource.clip != runSoundEffect && runSoundEffect != null)
            {
                footstepAudioSource.clip = runSoundEffect;
                footstepAudioSource.pitch = 1.1f;
                footstepAudioSource.Play();
            }
        }
        lastSpeedParam = speedParam;
        // --- End Footstep Sound Logic ---

        // Only move and rotate if there's actual input
        if (inputMagnitude > walkThreshold)
        {
            Vector3 moveAmount = targetDirection * 4f * Time.deltaTime;
            transform.Translate(moveAmount, Space.World);

            if (targetDirection != Vector3.zero)
            {
                Quaternion targetRotation = Quaternion.LookRotation(targetDirection, Vector3.up);
                // Use a lower turn speed for smoother, more realistic turning
                transform.rotation = Quaternion.Slerp(
                    transform.rotation,
                    targetRotation,
                    smoothTurnSpeed * Time.deltaTime
                );
            }
        }
        else if (inputMagnitude <= walkThreshold && speedParam == 0f)
        {
            anim.SetFloat("Speed", 0f);
        }
    }

    private bool IsInHitReactionAnimation()
    {
        // Use AnimationManager to check if playing hit reaction animations
        if (animationManager != null)
        {
            return animationManager.IsPlayingAnimation(
                // Active animation reactions only
                AnimationNames.JabReaction,
                AnimationNames.HaymakerReaction,
                AnimationNames.SpinningBackfistReaction,
                AnimationNames.ElbowSmashReaction,
                AnimationNames.ChargedPunchReaction,
                AnimationNames.AirPunchReaction,
                AnimationNames.DropKickReaction,
                AnimationNames.LowKickReaction
                
                /* Commented out other reactions for future use
                // Original hit reactions
                AnimationNames.RightHookHitReaction,
                AnimationNames.BodyKickHitReaction,
                AnimationNames.HookHitReaction,
                AnimationNames.JabHitReaction,
                AnimationNames.LegKickHitReaction,
                AnimationNames.UppercutPowerHitReaction,
                AnimationNames.OverhandHitReaction,
                
                // Other new hit reactions
                AnimationNames.ChopReaction,
                AnimationNames.GroundPoundReaction,
                AnimationNames.AirKickReaction,
                AnimationNames.SignSwingReaction,
                AnimationNames.SignOverheadSlamReaction,
                AnimationNames.SignChargeAttackReaction
                */
            );
        }
        
        // Fall back to original implementation if AnimationManager is not available
        AnimatorClipInfo[] m_CurrentClipInfo;
        m_CurrentClipInfo = gameObject.GetComponent<Animator>().GetCurrentAnimatorClipInfo(0);

        if (m_CurrentClipInfo.Length == 0)
        {
            // No clips are currently playing
            return false;
        }

        m_ClipName = m_CurrentClipInfo[0].clip.name;

        // Check for active animation reactions only
        return m_ClipName == AnimationNames.JabReaction ||
               m_ClipName == AnimationNames.HaymakerReaction ||
               m_ClipName == AnimationNames.SpinningBackfistReaction ||
               m_ClipName == AnimationNames.ElbowSmashReaction ||
               m_ClipName == AnimationNames.ChargedPunchReaction ||
               m_ClipName == AnimationNames.AirPunchReaction ||
               m_ClipName == AnimationNames.DropKickReaction ||
               m_ClipName == AnimationNames.LowKickReaction;
               
        /* Commented out other reactions for future use
               m_ClipName == AnimationNames.RightHookHitReaction || 
               m_ClipName == AnimationNames.BodyKickHitReaction ||
               m_ClipName == AnimationNames.HookHitReaction || 
               m_ClipName == AnimationNames.JabHitReaction ||
               m_ClipName == AnimationNames.LegKickHitReaction || 
               m_ClipName == AnimationNames.UppercutPowerHitReaction ||
               m_ClipName == AnimationNames.OverhandHitReaction ||
               
               // Other new hit reactions
               m_ClipName == AnimationNames.ChopReaction ||
               m_ClipName == AnimationNames.GroundPoundReaction ||
               m_ClipName == AnimationNames.AirKickReaction ||
               m_ClipName == AnimationNames.SignSwingReaction ||
               m_ClipName == AnimationNames.SignOverheadSlamReaction ||
               m_ClipName == AnimationNames.SignChargeAttackReaction;
        */
    }
    
    /// <summary>
    /// Updates footstep sounds based on animator Speed parameter.
    /// Called for all characters (including AI) to ensure footstep sounds play.
    /// </summary>
    private void UpdateFootstepSoundsFromAnimator()
    {
        if (anim == null || footstepAudioSource == null) return;
        
        // Read current speed from animator
        float speedParam = anim.GetFloat("Speed");
        
        // Use a slight threshold to avoid noise
        if (speedParam < 0.05f)
        {
            if (footstepAudioSource.isPlaying)
            {
                footstepAudioSource.Stop();
            }
            isFootstepPlaying = false;
        }
        else if (speedParam > 0.05f && !isFootstepPlaying)
        {
            // Start playing appropriate sound
            if (speedParam < 0.7f && walkSoundEffect != null)
            {
                footstepAudioSource.clip = walkSoundEffect;
                footstepAudioSource.pitch = 1f;
                footstepAudioSource.Play();
                isFootstepPlaying = true;
            }
            else if (speedParam >= 0.7f && runSoundEffect != null)
            {
                footstepAudioSource.clip = runSoundEffect;
                footstepAudioSource.pitch = 1.1f;
                footstepAudioSource.Play();
                isFootstepPlaying = true;
            }
        }
        else if (isFootstepPlaying)
        {
            // Switch between walk/run sound if needed
            if (speedParam < 0.7f && footstepAudioSource.clip != walkSoundEffect && walkSoundEffect != null)
            {
                footstepAudioSource.clip = walkSoundEffect;
                footstepAudioSource.pitch = 1f;
                footstepAudioSource.Play();
            }
            else if (speedParam >= 0.7f && footstepAudioSource.clip != runSoundEffect && runSoundEffect != null)
            {
                footstepAudioSource.clip = runSoundEffect;
                footstepAudioSource.pitch = 1.1f;
                footstepAudioSource.Play();
            }
        }
        
        lastSpeedParam = speedParam;
    }
}
