using Godot;
using System;


public partial class ai_fighter : ship
{
    [Signal]
    public delegate void OnDeathEventHandler(int factionInt);
    
    [Export]
    public float AggroRange = 500f;
    [Export]
    public int ScorePayout = 50;
    [Export]
    public float EngageDistance = 300f;

    public bool SignalsConnected { get; set; } = false;

    private CollisionShape2D _hitbox;
    public float HitboxModifier = 1f;
    
    private Label _healthLabel;
    private Label _hpLabel;

    //retreat logic
    private float _previousDistance;
    private float _stuckTime;
    private const float StuckThreshold = 0.5f;
    private const float DistanceTolerance = 5f;

    private Node2D _currentTarget;

    public override void _Ready()
    {
        _hitbox = GetNode<CollisionShape2D>("CollisionShape2D");
        _hitbox.Scale = new Vector2(HitboxModifier, HitboxModifier);
        
        SetShipStats();
        SetupVisual();
        Sprite.Texture = GD.Load<Texture2D>(SpritePath);
        
        _healthLabel = GetNode<Label>("HealthDisplay/HealthLabel");
        UpdateHealthLabel(Health);

        LaserSpawn = GetNode<Node2D>("LaserSpawn");
    }

    public override void _Process(double delta)
    {
        base._Process(delta);
        _healthLabel.GetParent<Node2D>().GlobalRotation = 0f;
    }

    public override void _PhysicsProcess(double delta)
    {
        UpdateMovement(delta);
        HandleFiring(delta);
        //GD.Print(Name, ": MainSpeed = ", MainSpeed, "  Velocity = ", Velocity.Length());
    }

    // SETUP
    public void UpdateHealthLabel(int health)
    {
        _healthLabel.Text = health.ToString();
        float percentage =  (float)Health / MaxHealth;

        _healthLabel.Modulate = percentage switch
        {
            <= 0.25f => new Color(1f, 0f, 0f),
            <= 0.5f => new Color(1f, 0.5f, 0f),
            <= 0.75f => new Color(1f, 1f, 0f),
            _ => new Color(0.07f,0.6f, 0.07f)
        }; // thank you rider
    }

    // COMBAT
    public override void ShipDamage(int damage)
    {
        base.ShipDamage(damage);
        UpdateHealthLabel(Health);
    }

    protected override async void Explode()
    {
        int factionInt = (int)Faction;
        EmitSignal(SignalName.OnDeath, factionInt);
        GetNode<AudioStreamPlayer2D>("ExplodeSFX").Play();
        await ToSignal(GetTree().CreateTimer(0.5f), "timeout");
        QueueFree();
    }

    private void HandleFiring(double delta)
    {
        
        FireTimer -= (float)delta; // countdown to next shot available
        if (FireTimer > 0f) return; // if countdown not finished then no shoot

        if (_currentTarget == null || !IsInstanceValid(_currentTarget)) return; // checks for valid target, returns if not

        // determining direction and distance to target
        Vector2 toTarget = (_currentTarget.GlobalPosition - GlobalPosition).Normalized();
        float angleToTarget = toTarget.Angle() + Mathf.Pi / 2;
        float angleDiff = Mathf.Abs(Mathf.AngleDifference(Rotation, angleToTarget));
        float distance = GlobalPosition.DistanceTo(_currentTarget.GlobalPosition);

        if (angleDiff < 0.4f && distance <= EngageDistance + 200f) // fire when in range and mostly aligned
        {
            ShootLaser();
            FireTimer = FireCooldown; // resets cooldown
        }
    }

    // TARGETING
    private Node2D FindClosestTarget() // returns nearest valid ship depending on faction
    {
        Node2D closest = null;
        float closestDistance = Mathf.Inf;

        Node shipParent = null;
        if (Faction == ShipFaction.FRIENDLY)
        {
            shipParent = GetTree().Root.GetNode("Game/WaveController/Ships/Enemy");
        }
        else if (Faction == ShipFaction.ENEMY || Faction == ShipFaction.ACE)
        {
            shipParent = GetTree().Root.GetNode("Game/WaveController/Ships/Friendly");
        }
        if (shipParent == null)
        {
            //GD.Print("No shipParent found");
            return null;
        }

        foreach (Node node in shipParent.GetChildren())
        {
            if (node is ship { Health: > 0 } target)
            {
                float dist = GlobalPosition.DistanceTo(target.GlobalPosition);
                if (dist < closestDistance)
                {
                    closest = target;
                    closestDistance = dist;
                }
            }
        }
        return closest;
    }

    private bool EnsureTarget() // finds a new target if no current or if invalid (dead)
    {
        if (_currentTarget == null || !IsInstanceValid(_currentTarget))
        {
            _currentTarget = FindClosestTarget();
            return false;
        }
        return true;
    }

    private void UpdateDistanceTracking(float distance) // tracks how long AI has been stuck in same spot
    {
        if (MathF.Abs(distance - _previousDistance) < DistanceTolerance)
        {
            _stuckTime += (float)GetProcessDeltaTime();
        }
        else
        {
            _stuckTime = 0f;
        }
        _previousDistance = distance;
    }

    private void HandleThrust(float angleDiff, float distance)
    {

        //GD.Print(Name, " | angleDiff: ", Mathf.RadToDeg(angleDiff), " | velocity: ", Velocity.Length());

        float retreatThreshold = EngageDistance * 0.75f; // retreat distance

        if (Mathf.Abs(angleDiff) < 1f) // only move forward when facing the target roughly
        {
            if (distance > EngageDistance)
            {
                Velocity += -Transform.Y * MainSpeed; // approach target
            }
            else if (distance < retreatThreshold)
            {
                if (_stuckTime > StuckThreshold)
                {
                    // reposition if stuck
                    Vector2 away = (GlobalPosition - _currentTarget.GlobalPosition).Normalized();
                    Velocity += away * MainSpeed;
                }
                else
                {
                    Velocity += Transform.X * StrafeSpeed; // try and orbit target laterally (strafe)
                }
            }
            else
            {
                Velocity += Transform.X * StrafeSpeed * 0.5f; // lateral drift in hold range
            }
        }
    }

    private void RotateToTarget(Vector2 direction, double delta) // rotates ship toward angle using rotation speed
    {
        float targetAngle = direction.Angle() + Mathf.Pi / 2;
        float angleDiff = Mathf.AngleDifference(Rotation, targetAngle);
        Rotation += Mathf.Clamp(angleDiff, -RotationSpeed * (float)delta, RotationSpeed * (float)delta);
    }

    private void UpdateMovement(double delta)
    {
        if (!EnsureTarget()) return; // skip if no valid target

        // math for direction, distance and rotation
        Vector2 direction = (_currentTarget.GlobalPosition - GlobalPosition).Normalized();
        float angleToTarget = direction.Angle() + MathF.PI / 2;
        float angleDiff = Mathf.AngleDifference(Rotation, angleToTarget);
        float distance = GlobalPosition.DistanceTo(_currentTarget.GlobalPosition);

        UpdateDistanceTracking(distance);
        HandleThrust(angleDiff, distance);
        RotateToTarget(direction, delta);

        Velocity = Velocity.MoveToward(Vector2.Zero, 2.5f); //2.5f FA value (on for AI)
        Velocity = Velocity.LimitLength(MaxSpeed);
        
        MoveAndSlide();
    }
}
