using Cirno.Scripts.Components.Actors;
using Godot;

namespace Cirno.Scripts.Components.FSM._3DPlayer;

public partial class IsoMovementModule : ModuleBase<PlayerState, CharacterBody3D>
{
    [Export] public IsoPlayerStorageModule  PlayerStorage { get; private set; } 
    [Export] private InputProvider _inputProvider;
    
    [Export] public int Speed { get; set; } = 45;
    [Export] public int StrafeSpeed { get; set; } = 35;
    [Export] public float Acceleration = 8f;
    [Export] public float Deceleration = 8f;
    [Export] public float Gravity = -9.8f;
    [Export] public float FallSpeed = 20f;
    
    private bool _isStrafing;
    private float _accelerationPerSecond;
    private float _decelerationPerSecond;

    public int MovementSpeed => _isStrafing ? StrafeSpeed : Speed;

    private IStateMachine<PlayerState, CharacterBody3D> _stateMachine;

    private CharacterBody3D MainObject => _stateMachine.MainObject;
    
    public override void EnterState(PlayerState state)
    {
        _accelerationPerSecond = Speed / Acceleration;
        _decelerationPerSecond = Speed / Deceleration;
    }

    public override void ExitState(PlayerState state)
    {
        
    }

    public override void Init(IStateMachine<PlayerState, CharacterBody3D> machine)
    {
        _stateMachine = machine;
    }

    public override void Process(double delta)
    {
        var movementInput = _inputProvider.GetMovementInput();
        
        _isStrafing = _inputProvider.GetStrafePressed();
        var rightStickInput = _inputProvider.GetAimInput().Normalized();
        
        // Update Facing Direction
        // if (!_isStrafing)
        // {
        if (rightStickInput.Length() > 0.1f) // If the right stick is moved
        {
            PlayerStorage.FacingDirection = rightStickInput;
        }
        else if (movementInput != Vector2.Zero) // Fall back to movement direction
        {
            PlayerStorage.FacingDirection = movementInput;
        }
        // }

        var rotatedMovementDirection = movementInput.Rotated(Mathf.DegToRad(-45f));
        
        PlayerStorage.MovementDirection = new Vector3(rotatedMovementDirection.X, 0, rotatedMovementDirection.Y);
    }

    public override void PhysicsProcess(double delta)
    {
     
        var frameVelocity = MainObject.Velocity;
        
        if (_isStrafing)
        {
            // Instant movement at strafe speed
            frameVelocity = PlayerStorage.MovementDirection * StrafeSpeed;
        }
        else
        {
            Vector3 targetVelocity = PlayerStorage.MovementDirection * Speed;

            if (PlayerStorage.MovementDirection != Vector3.Zero)
            {
                frameVelocity = frameVelocity.MoveToward(targetVelocity, Acceleration * (float)delta);
            }
            else
            {
                frameVelocity = frameVelocity.MoveToward(Vector3.Zero, Deceleration * (float)delta);
            }
        }
        
        //MainObject.Velocity += _movementDirection * MovementSpeed;

        var velocityY = Mathf.Clamp(frameVelocity.Y + Gravity * (float)delta, -FallSpeed, FallSpeed);

        frameVelocity.Y = velocityY;
        
        MainObject.Velocity = frameVelocity;
        
        MainObject.MoveAndSlide();
    }
}