using UnityEngine;
using Cinemachine;
using TMPro;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using System.Collections;

public class PlayerMovementManager : MonoBehaviour
{
    public float PlayerSpeed;
    [Header("References")]
    [SerializeField] public FloatingJoystick joystick;
    [SerializeField] public Rigidbody rb;
    [SerializeField] public Canvas inputCanvas;
    [SerializeField] public Transform footDirectionCircle, footDirection;
    [SerializeField] public CinemachineVirtualCamera mainCamera;
    [SerializeField] public TextMeshProUGUI speedDisplayText;
    [SerializeField] private Animator animator;


    [Header("Trail Renderer")]
    public bool enableTrailRenderer = false;
    private TrailRenderer trailRenderer;

    [Header("Movement Settings")]
    [SerializeField] public float movementSpeed = 8.3f, verticalSpeedMultiplier = 1.1f;
    [SerializeField] public float movementDelay = 0f, deceleration = 50f;
    [SerializeField] public float velocityLerp = 5f;
    [SerializeField, Range(0f, 0.5f)] public float minSpeedMultiplier = 0.2f;
    [SerializeField, Range(0.5f, 2f)] public float maxSpeedMultiplier = 1f;

    [Header("Input Settings")]
    [SerializeField, Range(0, 5)] public int inputDelayFrames = 0;
    [SerializeField, Range(0, 1)] public const float inputDeadzone = 0.01f;

    [Header("Rotation Settings")]
    [SerializeField] public float baseTurnSpeed = 25f;
    [SerializeField] public float rotationCompletionThreshold = 3f;
    [SerializeField] public float rotationDampingFactor = 1.0f; // slows down rotation when joystick is near (0, 0)
    [SerializeField]
    public AnimationCurve rotationResponseCurve = new AnimationCurve(
    new Keyframe(0f, 0.3f),   
    new Keyframe(45f, 0.3f),
    new Keyframe(90f, 1.0f),  
    new Keyframe(135f, 1.5f),
    new Keyframe(180f, 2.5f) 
);
    [SerializeField] public float maxTurnSpeed = 720f;
    [SerializeField] public float turnRounding = 0.7f;
    [SerializeField] public float snappingReductionFactor = 10f;

    [Header("Opponent Rotation Settings")]
    [SerializeField] public float opponentRotationSpeed = 2.0f; // Speed of rotation towards opponent (lower = more gradual)

    [Header("Collision Settings")]
    [SerializeField, Range(0f, 1f)] public float wallSlowdownFactor = 0.6f;
    [SerializeField] public float speedRecoveryTime = 0.75f;
    [SerializeField] public float wallPushForce = 0f, wallDrag = 0.5f;

    [Header("Foot Direction")]
    [SerializeField] public float footDirectionRange = 1.3f;
    [SerializeField] public float footDirectionYOffset = 2f, footDirectionZOffset = -2f;
    [SerializeField] public float footDirectionSpeedMultiplier = 4.0f;

    private Vector3 targetMovementDirection, smoothedMovementDirection, lastNonZeroDirection, previousDirection = Vector3.forward;
    private float currentSpeedMultiplier = 1f, speedRecoveryTimer, currentTurningBoost;
    private bool isTouchingWall, wasCollidingWithWall, isGrounded, isJoystickInputEnabled;
    public bool isInitialTouch, hasMovedDuringTouch;
    private Vector2 initialTouchPosition;
    private Queue<Vector2> inputBuffer = new Queue<Vector2>();
    private Vector2 currentDelayedInput;
    private bool isRotationComplete = false;
    private float currentTurnVelocity; // For smooth damp
    private Quaternion targetRotation;
    private Vector3 velocityRef;
    private int rotationTimes = 0;
    private bool wasMoving = false;
    private bool isFacingOpponentSmoothly = false;



    public bool IsMoving { get; private set; }
    public float CurrentSpeed { get; private set; }
    public GameObject Opponent { get; set; }

    private void Start()
    {
        if (IsOpponentDeadOrMissing())
            Opponent = GameObject.FindGameObjectWithTag("Opponent");

        EnableJoystickInput();
        rb = rb ? rb : GetComponent<Rigidbody>();
        rb.freezeRotation = true;
        trailRenderer = GetComponent<TrailRenderer>();

        var eventChannel = FindObjectOfType<EnemyEventChannel>();
        if (eventChannel != null)
        {
            eventChannel.OnEnemySpawned += (newEnemy) => {
                Opponent = newEnemy;
                Debug.Log($"New opponent assigned: {newEnemy.name}");
            };
        }
    }

    public void EnableJoystickInput()
    {
        isJoystickInputEnabled = true;
        inputCanvas.gameObject.SetActive(true);
    }

    private void Update()
    {
        if (!isJoystickInputEnabled) return;
        trailRenderer.enabled = enableTrailRenderer;

        HandleWallCollision();
        UpdateFootDirection();
        HandleTouchInput();
        ProcessInputBuffer();
    }

    private void FixedUpdate()
    {
        if (!isJoystickInputEnabled) return;

        HandleMovement();

        if (IsMoving)
        {
            HandleRotation(); // Only handle input-based rotation when moving
        }
        else
        {
            RotateTowardsOpponent(); // Only rotate towards opponent when not moving
        }

        PlayerSpeed = rb.velocity.magnitude;

    }
    private void FaceOpponentSmooth()
    {
        if (IsOpponentDeadOrMissing()) return;

        Vector3 directionToOpponent = Opponent.transform.position - transform.position;
        directionToOpponent.y = 0f;

        if (directionToOpponent.sqrMagnitude > 0.01f)
        {
            Quaternion targetRot = Quaternion.LookRotation(directionToOpponent);
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, Time.deltaTime * opponentRotationSpeed);
        }
    }

    private void FaceOpponent()
    {
        if (IsOpponentDeadOrMissing()) return;

        Vector3 directionToOpponent = Opponent.transform.position - transform.position;
        directionToOpponent.y = 0f;

        if (directionToOpponent.sqrMagnitude < 0.0001f)
            return;

        Quaternion desiredRotation = Quaternion.LookRotation(directionToOpponent);
        float angleDifference = Quaternion.Angle(transform.rotation, desiredRotation);

        if (angleDifference > 0.1f)
        { 
            float lerpSpeed = 5f; 
            transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, Time.deltaTime * lerpSpeed);
        }
        else
        {
            transform.rotation = desiredRotation;
        }
    }

    private void HandleTouchInput()
    {
        bool touching = UnityEngine.Input.touchCount > 0 || UnityEngine.Input.GetMouseButton(0);

        if (touching)
        {
            if (!isInitialTouch)
            {
                isInitialTouch = true;
                hasMovedDuringTouch = false;
                initialTouchPosition = GetCurrentInputPosition();
            }
            else
            {
                hasMovedDuringTouch = Vector2.Distance(GetCurrentInputPosition(), initialTouchPosition) > 2f;
            }
        }
        else
        {
            isInitialTouch = false;
            hasMovedDuringTouch = false;
        }
    }

    private Vector2 GetCurrentInputPosition() =>
        UnityEngine.Input.touchCount > 0 ?
            UnityEngine.Input.touches[0].position :
            (Vector2)UnityEngine.Input.mousePosition;

    private void ProcessInputBuffer()
    {
        Vector2 input = new Vector2(joystick.Horizontal, joystick.Vertical);
        inputBuffer.Enqueue(input);

        while (inputBuffer.Count > inputDelayFrames + 1)
            currentDelayedInput = inputBuffer.Dequeue();

        if (inputDelayFrames == 0)
            currentDelayedInput = input;
    }

    private void HandleWallCollision()
    {
        isTouchingWall = Physics.CheckSphere(transform.position, 0.75f, LayerMask.GetMask("Wall"));

        if (wasCollidingWithWall && !isTouchingWall)
        {
            speedRecoveryTimer = speedRecoveryTime;
            currentSpeedMultiplier = wallSlowdownFactor;
        }
        else if (!isTouchingWall)
        {
            currentSpeedMultiplier = 1f;
            speedRecoveryTimer -= Time.deltaTime;
        }

        wasCollidingWithWall = isTouchingWall;
    }

    private void HandleMovement()
    {
        Vector2 input = currentDelayedInput;

        bool active = input.magnitude > inputDeadzone || joystick.isRunning;
        bool runningInPlace = isInitialTouch && !hasMovedDuringTouch && input.magnitude <= inputDeadzone;

        IsMoving = active || runningInPlace;

        if (!IsMoving)
        {
            rb.velocity = Vector3.Lerp(
                rb.velocity,
                Vector3.zero,
                Time.fixedDeltaTime * velocityLerp
            );

            rb.angularVelocity = Vector3.Lerp(
                rb.angularVelocity,
                Vector3.zero,
                Time.fixedDeltaTime * velocityLerp
            );
            if (rb.velocity.magnitude < 0.1f)
            {
                rb.velocity = Vector3.zero;
                CurrentSpeed = 0f;
            }
            return;
        }
 

        UpdateMovementDirection(input, runningInPlace);
        ApplyMovement(input, runningInPlace);
    }

    private void UpdateMovementDirection(Vector2 input, bool inPlace)
    {
        if (inPlace || input.magnitude <= inputDeadzone)
        {
            targetMovementDirection = transform.forward;
            lastNonZeroDirection = transform.forward;
        }
        else
        {
            Vector3 newDirection = new Vector3(input.x, 0, input.y).normalized;
            lastNonZeroDirection = Vector3.Slerp(lastNonZeroDirection, newDirection, input.magnitude * turnRounding * snappingReductionFactor); //prevents snapping

            targetMovementDirection = lastNonZeroDirection;
        }

        smoothedMovementDirection = Vector3.Lerp(smoothedMovementDirection, targetMovementDirection, Time.fixedDeltaTime / movementDelay);
    }

    private void ApplyMovement(Vector2 input, bool inPlace)
    {
        float inputMag = inPlace ? 0.5f : Mathf.Clamp01(input.magnitude);
        CurrentSpeed = inPlace ? 0f : movementSpeed * currentSpeedMultiplier;

        Vector3 targetVelocity = new Vector3(
            smoothedMovementDirection.x * CurrentSpeed,
            rb.velocity.y,
            smoothedMovementDirection.z * CurrentSpeed * verticalSpeedMultiplier
        );

        rb.velocity = targetVelocity;

        if (isTouchingWall && (inputMag > inputDeadzone || joystick.isRunning))
            ApplyWallPush(inputMag);
    }

    private void ApplyWallPush(float magnitude)
    {
        Vector3 push = lastNonZeroDirection * Mathf.Max(magnitude, 0.3f) * wallPushForce;
        rb.AddForce(push, ForceMode.Force);
        rb.velocity = new Vector3(rb.velocity.x, rb.velocity.y * 0.5f, rb.velocity.z);
    }

    private void ApplyDeceleration()
    {
        CurrentSpeed = 0f;
        rb.AddForce(new Vector3(-rb.velocity.x, 0, -rb.velocity.z) * deceleration, ForceMode.Acceleration);
    }

    private void HandleRotation()
    {
        if (!IsMoving) return;

        if (lastNonZeroDirection.magnitude > 0.1f)
        {
            targetRotation = Quaternion.LookRotation(lastNonZeroDirection);
            float angleDifference = Quaternion.Angle(rb.rotation, targetRotation);
            float responseMultiplier = rotationResponseCurve.Evaluate(angleDifference);

            if (currentDelayedInput.magnitude <= inputDeadzone * 1.5f)
            {
                responseMultiplier *= rotationDampingFactor;
            }

            float turnFactor;

            if (angleDifference > 90f)
            {
                turnFactor = Mathf.Lerp(0.3f, 0.9f, responseMultiplier / 2.5f); //adjust here to change the lerp speed when turning on big angle differences

                if (rb.velocity.magnitude > 0.1f)
                {
                    float speedReduction = (angleDifference - 90f) / 300f;
                    rb.velocity *= (1f - speedReduction);
                }

                rb.rotation = Quaternion.Slerp(rb.rotation, targetRotation, turnFactor);
            }
            else
            {
                float turnSpeed = baseTurnSpeed * responseMultiplier;
                turnSpeed = Mathf.Min(turnSpeed, maxTurnSpeed);
                turnFactor = Mathf.Clamp01(turnSpeed * Time.fixedDeltaTime);

                rb.rotation = Quaternion.Lerp(rb.rotation, targetRotation, turnFactor);
            }

            isRotationComplete = angleDifference <= rotationCompletionThreshold;
        }
        else
        {
            isRotationComplete = true;
        }
    }

    private void UpdateFootDirection()
    {
        if (!footDirection || !footDirectionCircle) return;

        Vector2 currentInput = new Vector2(joystick.Horizontal, joystick.Vertical);
        
        footDirection.position = new Vector3(
            footDirectionCircle.position.x + currentInput.x * footDirectionRange,
            footDirectionYOffset,
            footDirectionCircle.position.z + footDirectionZOffset + currentInput.y * footDirectionRange
        );
    }

    public void RotateTowardsOpponent()
    {
        if (IsOpponentDeadOrMissing()) return;

        Vector3 directionToOpponent = Opponent.transform.position - transform.position;
        directionToOpponent.y = 0f;

        if (directionToOpponent.magnitude > 0.1f)
        {
            //rb.rotation = Quaternion.LookRotation(directionToOpponent);

            //rotationTimes++;
            //Debug.Log("Rotating towards opponent: " + rotationTimes);


            //============================================================================================================= ^^ dont touch
            Quaternion targetRotation = Quaternion.LookRotation(directionToOpponent);
            float angle = Quaternion.Angle(transform.rotation, targetRotation);

            if (angle < 2f)
            {
                transform.rotation = targetRotation;
                rb.rotation = targetRotation;
                return;
            }
            else
            {
                Quaternion newRotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime * opponentRotationSpeed);
                transform.rotation = newRotation;
                rb.rotation = newRotation;
            }

            rotationTimes++;
            Debug.Log("Rotating towards opponent: " + rotationTimes);
        
        }

    }
    public bool IsOpponentDeadOrMissing()
    {
        if (Opponent == null) return true;
        var monsterHit = Opponent.GetComponent<MonsterHit>();
        if (monsterHit == null || monsterHit.monsterAnimator == null) return true;
        return monsterHit.monsterAnimator.GetBool("isDie");
    }


    private void OnDrawGizmos()
    {
        if (Opponent == null) return;

        Gizmos.color = Color.red;
        Gizmos.DrawSphere(Opponent.transform.position, 0.5f);
        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(transform.position, Opponent.transform.position);
        Gizmos.color = Color.blue;
        Vector3 forwardLineEnd = transform.position + transform.forward * 10f;
        Gizmos.DrawLine(transform.position, forwardLineEnd);
    }
}

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

public class EnemySpawnerButton : MonoBehaviour
{
    [Header("Spawning Settings")]
    [SerializeField] private GameObject enemyPrefab;
    [SerializeField] private Vector3 spawnPosition = new Vector3(0f, 0f, 6.04f);
    [SerializeField] private Button spawnButton;

    private void Start()
    {
        if (spawnButton == null)
            spawnButton = GetComponent<Button>();

        if (spawnButton != null)
        {
            spawnButton.onClick.AddListener(SpawnEnemy);
        }
        else
        {
            Debug.LogError("No Button component found! Please assign a button or attach this script to a Button GameObject.");
        }
    }

    public void SpawnEnemy()
    {
        if (enemyPrefab != null)
        {
            GameObject spawnedEnemy = Instantiate(enemyPrefab, spawnPosition, Quaternion.identity);
            // Notify through event channel
            var eventChannel = FindObjectOfType<EnemyEventChannel>();
            if (eventChannel != null)
            {
                eventChannel.RaiseEnemySpawned(spawnedEnemy);
            }
            else // Fallback if event system isn't set up
            {
                PlayerMovementManager player = FindObjectOfType<PlayerMovementManager>();
                if (player != null) player.Opponent = spawnedEnemy;
            }
        }
        else
        {
            Debug.LogError("Enemy prefab is not assigned! Please assign an enemy prefab in the inspector.");
        }
    }

    private void OnDestroy()
    {
        if (spawnButton != null)
        {
            spawnButton.onClick.RemoveListener(SpawnEnemy);
        }
    }

    public void SetSpawnPosition(Vector3 newPosition)
    {
        spawnPosition = newPosition;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyEventChannel : MonoBehaviour
{
    public static EnemyEventChannel Instance { get; private set; }
    public event System.Action<GameObject> OnEnemySpawned;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject); // Optional: persists across scenes
        }
        else
        {
            Destroy(gameObject);
        }
    }

    public void RaiseEnemySpawned(GameObject enemy)
    {
        OnEnemySpawned?.Invoke(enemy);
    }
}
why does my player not recognise and start to shooot arrows at enemies that are spawned on clicking of the enemy spawner button?