using AceFieldNewHorizon.Scripts.System;
using Godot;

namespace AceFieldNewHorizon.Scripts.Entities;

public partial class Player : CharacterBody2D
{
    [Export] public float MaxSpeed = 400.0f;
    [Export] public float SprintMultiplier = 1.8f; // 80% faster when sprinting
    [Export] public float Acceleration = 1500.0f;
    [Export] public float SprintAcceleration = 1800.0f; // Slightly faster acceleration when sprinting
    [Export] public float Deceleration = 1200.0f;
    [Export] public float RotationSpeed = 3.0f;
    [Export] public float MinZoom = 0.5f;
    [Export] public float MaxZoom = 2.0f;
    [Export] public float BaseZoomSpeed = 0.1f;
    [Export] public float MaxZoomSpeed = 0.5f;
    [Export] public float ZoomAcceleration = 0.05f;
    [Export] public float ZoomDecay = 0.9f;
    [Export] public float ZoomSmoothing = 10.0f;

    public ResourceManager Inventory { get; private set; }
        
    private Camera2D _camera;
    private Vector2 _cameraTargetZoom = Vector2.One;
    private float _currentZoomSpeed;
    private int _lastZoomDirection;
    private double _lastZoomTime;

    public override void _Ready()
    {
        _camera = GetNode<Camera2D>("Camera2D");
        _cameraTargetZoom = _camera.Zoom;
        
        Inventory = DependencyInjection.Container.GetInstance<ResourceManager>();
        
        AddToGroup(ItemPickup.PickupGroupName);
        AddToGroup(NaturalResourceGenerator.ChunkTrackerGroupName);
    }
    
    public override void _Input(InputEvent @event)
    {
        // Handle mouse wheel zoom
        if (@event is InputEventMouseButton mouseEvent)
        {
            switch (mouseEvent.ButtonIndex)
            {
                case MouseButton.WheelDown when mouseEvent.Pressed:
                    HandleZoomInput(1);
                    break;
                case MouseButton.WheelUp when mouseEvent.Pressed:
                    HandleZoomInput(-1);
                    break;
            }
        }
    }
    
    private void HandleZoomInput(int direction)
    {
        var currentTime = Time.GetTicksMsec();
        
        // If same direction as last time, accelerate
        if (direction == _lastZoomDirection && (currentTime - _lastZoomTime) < 300)
            _currentZoomSpeed = Mathf.Min(_currentZoomSpeed + ZoomAcceleration, MaxZoomSpeed);
        else
            _currentZoomSpeed = BaseZoomSpeed;
        
        _lastZoomDirection = direction;
        _lastZoomTime = currentTime;
        
        // Apply zoom with current speed
        var zoomFactor = 1.0f + (_currentZoomSpeed * -direction);
        _cameraTargetZoom = _camera.Zoom * zoomFactor;
    
        // Clamp target zoom
        _cameraTargetZoom.X = Mathf.Clamp(_cameraTargetZoom.X, MinZoom, MaxZoom);
        _cameraTargetZoom.Y = Mathf.Clamp(_cameraTargetZoom.Y, MinZoom, MaxZoom);
    }

    public override void _Process(double delta)
    {
        // Get direction to mouse and calculate angle
        var mousePos = GetGlobalMousePosition();
        var direction = GlobalPosition.DirectionTo(mousePos);
        Rotation = direction.Angle();
        
        // Smoothly interpolate to target zoom
        var deltaF = (float)delta;
        _camera.Zoom = _camera.Zoom.Lerp(_cameraTargetZoom, ZoomSmoothing * deltaF);
        
        // Decay zoom speed when not zooming
        if ((Time.GetTicksMsec() - _lastZoomTime) > 300)
        {
            _currentZoomSpeed = Mathf.Max(_currentZoomSpeed * ZoomDecay, BaseZoomSpeed);
        }
    }

    public override void _PhysicsProcess(double delta)
    {
        // Get movement input
        var moveForward = Input.GetActionStrength("move_up") - Input.GetActionStrength("move_down");
        var moveHorizontal = Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left");
        var isSprinting = Input.IsActionPressed("move_sprint");
        
        // Calculate movement parameters based on sprint state
        var currentMaxSpeed = isSprinting ? MaxSpeed * SprintMultiplier : MaxSpeed;
        var currentAcceleration = isSprinting ? SprintAcceleration : Acceleration;
        
        // Calculate desired movement direction
        var forwardVector = Vector2.Right.Rotated(Rotation);
        var rightVector = new Vector2(-forwardVector.Y, forwardVector.X);
        
        // Calculate target velocity based on input
        var targetVelocity = (forwardVector * moveForward + rightVector * moveHorizontal).Normalized() * currentMaxSpeed;
        
        // Apply acceleration or deceleration
        var currentSpeed = Velocity.Length();
        var isAccelerating = targetVelocity != Vector2.Zero;
        
        if (isAccelerating)
        {
            // Accelerate towards target velocity
            Velocity = Velocity.MoveToward(targetVelocity, (float)(currentAcceleration * delta));
        }
        else
        {
            // Apply deceleration when no input
            if (currentSpeed > 0)
            {
                var decelAmount = (float)(Deceleration * delta);
                if (currentSpeed <= decelAmount)
                    Velocity = Vector2.Zero;
                else
                    Velocity = Velocity.Normalized() * (currentSpeed - decelAmount);
            }
        }
        
        // Apply the movement
        MoveAndSlide();
    }
    
    public void AddItem(string itemId, int quantity)
    {
        Inventory.AddResource(itemId, quantity);
        GD.Print($"[Player] Picked up {quantity} x {itemId}. Total: {Inventory.GetResourceAmount(itemId)}");
    }
}