using Godot;
using Cirno.Scripts;
using Cirno.Scripts.Components;
using System.Collections.Generic;
using System.Linq;
using Cirno.Scripts.Components.Actors;
using Cirno.Scripts.Enums;

public partial class EnemyNavigationMovement : MovementHandler
{
    public override Vector2 FacingDirection
    {
        get => _parent.FacingDirection;
        set => _parent.FacingDirection = value;
    }

    public override Vector2 MovementDirection
    {
        get => _parent.MovementDirection;
        set => _parent.MovementDirection = value;
    }

    public bool IsDestroyed => _parent.IsDestroyed;

    [Export] private bool _navigationEnabled = false;

    [Export] public float AlarmReactRange = 200f;

    [Export] public float PlayerDisengageRange = 500f;

    [Export(PropertyHint.Layers2DPhysics)]
    public uint CollisionMask { get; set; }
    
    [Export] public Weapon EquippedWeapon;

    public bool NavigationEnabled
    {
        get => _actorAi is not null && _actorAi.Ai is AiState.Enabled && _navigationEnabled && _navigationAgent != null;
        set => _navigationEnabled = value;
    }

    [Export] private PlayerDetection _playerDetection;

    private ActorAi _actorAi;
    private NavigationAgent2D _navigationAgent;
    private AlarmManager _alarmManager;

    private Vector2? _lastPlayerPosition = null;

    private bool IsPlayerInRange => _playerDetection is { IsPlayerInRange: true };

    private bool IsPlayerInSight =>
        _playerDetection is not null && _playerDetection.IsPlayerInSight(CollisionMask);

    public override void Init(Actor parent)
    {
        base.Init(parent);

        MovementDirection = Vector2.Zero;
        FacingDirection = Vector2.Down;

        _actorAi = parent.GetNode<ActorAi>("ActorAi");
        _navigationAgent = _parent.GetNodeOrNull<NavigationAgent2D>("NavigationAgent2D");
        _alarmManager = this.GetAlarmManager();

        if (_alarmManager != null)
        {
            _alarmManager.AlarmEnabled += AlarmManagerOnAlarmEnabled;
        }
    }

    public override void Update(double delta)
    {
        
    }

    private void AlarmManagerOnAlarmEnabled(Vector2 location)
    {
        if (NavigationEnabled && location.DistanceTo(_parent.GlobalPosition) <= AlarmReactRange)
        {
            GD.Print($"Enemy {Name} alerted");
            _actorAi.State = EnemyState.Alert;
            _lastPlayerPosition = location;
        }
    }

    public override void PhysicsUpdate(double delta)
    {
        if (IsDestroyed) return;
        if (_actorAi.Ai is not AiState.Enabled)
            return;
        
        switch (_actorAi.State)
        {
            case EnemyState.Idle:
                if (_playerDetection != null && IsPlayerInSight)
                {
                    _actorAi.State = EnemyState.Alert;
                    GD.Print("Switching to alert");
                }

                break;
            case EnemyState.Alert:

                // Update last known player position if it's in range
                if (IsPlayerInRange)
                {
                    _lastPlayerPosition = _playerDetection.CachedPlayer.GlobalPosition;
                }

                if (NavigationEnabled)
                {
                    if (_lastPlayerPosition.HasValue)
                    {
                        _navigationAgent.SetTargetPosition(_lastPlayerPosition.Value);
                    }

                    var currentAgentPosition = _parent.GlobalPosition;

                    var nextPathPosition = _navigationAgent.GetNextPathPosition();

                    var newVelocity = currentAgentPosition.DirectionTo(nextPathPosition) * _parent.MovementSpeed;
                    
                    // Navigation is over, can do other things like shooting
                    if (_navigationAgent.IsNavigationFinished())
                    {
                        // Shoot player
                        if (IsPlayerInSight)
                        {
                            Shoot();
                        }

                        // TODO: If player totally left the max range it should stop shooting and go back to idle

                        return;
                    }

                    if (_navigationAgent.AvoidanceEnabled)
                    {
                        _navigationAgent.SetVelocity(newVelocity);
                    }
                    else
                    {
                        _on_navigation_agent_2d_velocity_computed(newVelocity);
                    }

                    _parent.MoveAndSlide();
                }
                else
                {
                    if (IsPlayerInSight)
                    {
                        Shoot();
                    }
                }
                break;
        }
    }

    private void Shoot()
    {
        if (EquippedWeapon == null || !_lastPlayerPosition.HasValue) return;
        
        var direction = (_lastPlayerPosition.Value - _parent.GlobalPosition).Normalized();
        
        // Shoot at the player's last known position
        EquippedWeapon.ShootDirection = direction;
        FacingDirection = direction;
        
        EquippedWeapon.Shoot();
    }

    public void _on_navigation_agent_2d_velocity_computed(Vector2 safeVelocity)
    {
        _parent.Velocity = safeVelocity;
        if (_parent.Velocity.Length() > 0)
        {
            _parent.FacingDirection = _parent.Velocity.Normalized();
        }
    }
}