using System;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using DG.Tweening;

public class NewInputPlatformer : MonoBehaviour
{
    enum PlayerState
    {
        Crouching=0, Running, Walking, Dashing, Attacking, Jumping, Dead, Idle  
    }
    
    [Header("Control parameters")]
    [SerializeField] private new Rigidbody2D rigidbody2D; 
    [SerializeField] private new Collider2D collider;
    [SerializeField] private Transform groundCheck, attackPos;
    [SerializeField] private float moveSpeed;
    [SerializeField] private float crouchMultiplier, sprintMultiplier;
    [SerializeField] private float jumpForce,dashSpeed;
    [SerializeField] private LayerMask groundMask, obstacleMask;
    [SerializeField] private Transform spawnPoint;
    [SerializeField] private Animator anim;

    [Header("Wrapping")] 
    [SerializeField] private int maxHP, maxStamina; 
    private int _healthPoints, _staminaPoints;
    
    private float _horizontal, _vertical,_currentSpeed;
    private PlayerState _state = PlayerState.Idle;
    private SpriteRenderer _sp;
    private Vector2 _velocity;
    private bool _isDead;
    private float _lastDash, _lastJump;
    [SerializeField] private float  dashCooldown, jumpCooldown = 2f;
    private bool _dashTrigger;
    public UnityEvent death, resurrect; 
    private bool _isGrounded;
    private void Awake()
    {
        rigidbody2D = gameObject.GetComponent<Rigidbody2D>();
        _sp = gameObject.GetComponentInChildren<SpriteRenderer>();
        collider = gameObject.GetComponent<Collider2D>();
        anim = gameObject.GetComponent<Animator>();
        _lastDash = float.MinValue;
        _lastJump = float.MinValue;
        _currentSpeed = moveSpeed;
    }
    
    private void Start()
    {
        if (spawnPoint)
        {
            transform.position = spawnPoint.position;
        }
        death.AddListener(Kill);
        resurrect.AddListener(Resurrect);
        anim.CrossFade("Idle",0f);
        _healthPoints = maxHP; 
        _staminaPoints = maxStamina;
    }
    
    
    private void Update()
    {
        _isGrounded = Physics2D.OverlapCircle(groundCheck.position,0.2f,groundMask);
    }

    private void FixedUpdate()
    {
        if (_isDead)
        {
            _state = PlayerState.Dead;
            return;
        }

        // Debug.Log("Player state: " + _state);
        Controls();
        StateManager();
        AnimationController();
    }

    private void Controls()
    {
        if (_horizontal == 0f)
        {
            return;
        }
        
        Vector2 localVelocity = rigidbody2D.velocity;

        // Movement behaviour
        _sp.flipX = _horizontal < 0; // Flip the sprite to face the direction
        if (!_dashTrigger)
        {
            localVelocity = new Vector2(_horizontal * _currentSpeed,localVelocity.y);
            _state = _state is PlayerState.Idle or PlayerState.Walking? PlayerState.Walking : _state;
        }
            
        rigidbody2D.velocity = localVelocity;
    }

    public void MoveInputs(InputAction.CallbackContext ctx)
    {
        _horizontal = ctx.ReadValue<Vector2>().x;
        _vertical = ctx.ReadValue<Vector2>().y;
        if (ctx.canceled)
        {
            _state = PlayerState.Idle;
        }
    }
    
    public void Dash(InputAction.CallbackContext ctx) // Works but is not that great
    {
        if (ctx.performed)
        {
            Vector2 localVelocity = rigidbody2D.velocity;
            if (Time.time - _lastDash < dashCooldown) return;
            
            _dashTrigger = true;
            _lastDash = Time.time;
            localVelocity = new Vector2( dashSpeed * _horizontal, localVelocity.y);
            _state = PlayerState.Dashing;
            rigidbody2D.velocity = localVelocity;
        }

        if (ctx.canceled)
        {
            _state = PlayerState.Idle;
        }
        
    }

    public void DashV2(InputAction.CallbackContext ctx) // Tweening version ! 
    {
        if (ctx.performed)
        {
            if (Time.time - _lastDash < dashCooldown) return;
            if (_dashTrigger) return; 
            _lastDash = Time.time;
            
            float distance = 4f;
            float dashTarget;
            
            RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.right * _horizontal, distance,obstacleMask);
            if (hit)
            {
                //Debug.Log("Hit object" + hit.collider.gameObject);
                distance = Mathf.Abs(hit.point.x - transform.position.x);
                dashTarget = hit.point.x;
            }
            else
            {
                dashTarget = transform.position.x + distance * _horizontal;
            }
            
            //Debug.Log(distance);
            transform.DOMoveX(dashTarget, 0.06f* distance).SetEase(Ease.Linear).onComplete += () => { 
                _dashTrigger = false;
                _state = PlayerState.Idle;
            };
            _dashTrigger = true;  
            _state = PlayerState.Dashing;
        }
        
        
    }

    public void Jump(InputAction.CallbackContext ctx)
    {
        Vector2 localVelocity = rigidbody2D.velocity;
 
        if (ctx.performed)
        {
            _state = PlayerState.Jumping;
            if (!_isGrounded) return;
            if (Time.time - _lastJump < jumpCooldown) return;
            
            _lastJump = Time.time;
            //Debug.Log("Jump");
            localVelocity = new Vector2(localVelocity.x, jumpForce);
            
        }
        if(ctx.canceled)
        {
            _state = PlayerState.Idle;
            //Debug.Log("Jump canceled");
            localVelocity = new Vector2(localVelocity.x, localVelocity.y * 0.5f);
        }

        if (_isGrounded)
        {
            _state = PlayerState.Idle;
        }
        
        rigidbody2D.velocity = localVelocity;
    }

    public void Crouch(InputAction.CallbackContext ctx)
    {
        if (ctx.performed)
        {
            _state = PlayerState.Crouching;
            //Debug.Log("Crouch");
            _currentSpeed = moveSpeed * crouchMultiplier;
        }

        if (ctx.canceled)
        {
            _state = PlayerState.Idle;
            _currentSpeed = moveSpeed;
            //Debug.Log("Crouch canceled");
        }
    }

    public void Sprint(InputAction.CallbackContext ctx)
    {
        if (ctx.performed)
        {   
            _state = PlayerState.Running;
            //Debug.Log("Sprint");
            _currentSpeed = moveSpeed * sprintMultiplier;
        }
        
        if (ctx.canceled)
        {
            _state = PlayerState.Idle;
            _currentSpeed = moveSpeed;
        }
    }

    public void Attack(InputAction.CallbackContext ctx)
    {
        if (ctx.performed)
        {
            _state = PlayerState.Attacking;
            //Debug.Log("Attack");
            _horizontal = 0f;
            Collider2D[] enemies = Physics2D.OverlapBoxAll(attackPos.position, new Vector2(0.72f, 1f), 0, LayerMask.GetMask("Enemy")); 
            foreach (Collider2D enemy in enemies)
            {
                //Debug.Log("Enemy name:" + enemy.gameObject.name);
            }
        }
    }

    private void StateManager()
    {
    }
    
    private void AnimationController() // Animation switch function to change between stances 
    {
        switch (_state)
        {
            case PlayerState.Crouching:
                anim.CrossFade("Crouch",0f);
                break;
            case PlayerState.Running:
                anim.CrossFade("Run",0f);
                break;
            case PlayerState.Walking:
                anim.CrossFade("Walk",0f);
                break;
            case PlayerState.Jumping:
                anim.CrossFade("Jump",0f);
                break;
            case PlayerState.Dead:
                anim.CrossFade("Crouch",0f);
                break;
            case PlayerState.Idle:
                anim.CrossFade("Idle",0f);
                break;
            case PlayerState.Dashing:
                anim.CrossFade("Slide",0f);
                break;
            case PlayerState.Attacking:
                anim.CrossFade("Attack",0f);
                break;
            default:
                anim.CrossFade("Idle",0f);
                throw new Exception("Unknown state");
        }
    }

    private void Kill()
    {
        _isDead = true; 
    }

    private void Resurrect()
    {
        _isDead  = false;
    }

    public void TakeDamage(int damage)
    {
        _healthPoints -= damage;
        if (_healthPoints <= 0)
        {
            death.Invoke();
        }
    }
}
