using System.Collections.Generic;
using AceFieldNewHorizon.Scripts.System;
using AceFieldNewHorizon.Scripts.Tiles;
using Godot;

namespace AceFieldNewHorizon.Scripts.Entities;

public abstract partial class BaseEnemy : CharacterBody2D
{
    public const string EnemyGroupName = "Enemy";

    [Export] public float MoveSpeed = 150.0f;
    [Export] public float DetectionRadius = 300.0f;
    [Export] public float AttackRadius = 80.0f;
    [Export] public int Damage = 10;
    [Export] public float AttackCooldown = 3.0f;
    [Export] public int MaxHealth = 100;
    [Export] public bool ShowDamageNumbers = true;

    protected BaseTile TargetTile;
    protected ReactorTile Reactor;
    protected float AttackTimer = 0;
    protected Area2D DetectionArea;
    protected Area2D AttackArea;
    protected int CurrentHealthValue;
    protected ProgressBar HealthBar;
    protected GridManager Grid;

    // Track collisions with potential targets
    protected readonly HashSet<BaseTile> CollidingTiles = [];

    public int CurrentHealth
    {
        get => CurrentHealthValue;
        protected set
        {
            CurrentHealthValue = Mathf.Clamp(value, 0, MaxHealth);
            UpdateHealthBar();

            if (CurrentHealthValue <= 0)
            {
                Die();
            }
        }
    }

    public bool IsDead => CurrentHealthValue <= 0;

    public override void _Ready()
    {
        CurrentHealthValue = MaxHealth;
        Grid = DependencyInjection.Container.GetInstance<GridManager>();
        Reactor = GetTree().GetFirstNodeInGroup(ReactorTile.ReactorGroupName) as ReactorTile;

        InitializeHealthBar();
        InitializeDetectionArea();
        InitializeAttackArea();
        
        AddToGroup(EnemyGroupName);
    }

    protected virtual void InitializeHealthBar()
    {
        HealthBar = new ProgressBar
        {
            MaxValue = MaxHealth,
            Value = CurrentHealthValue,
            Size = new Vector2(40, 4),
            ShowPercentage = false,
            Visible = false
        };

        var healthBarContainer = new Control();
        healthBarContainer.AddChild(HealthBar);
        AddChild(healthBarContainer);
        healthBarContainer.Position = new Vector2(-20, -20);
    }

    protected virtual void InitializeDetectionArea()
    {
        DetectionArea = new Area2D();
        var collisionShape = new CollisionShape2D();
        var shape = new CircleShape2D();
        shape.Radius = DetectionRadius;
        collisionShape.Shape = shape;
        DetectionArea.AddChild(collisionShape);
        AddChild(DetectionArea);

        DetectionArea.BodyEntered += OnBodyEnteredDetection;
        DetectionArea.BodyExited += OnBodyExitedDetection;
    }

    protected virtual void InitializeAttackArea()
    {
        AttackArea = GetNodeOrNull<Area2D>("AttackArea");
        if (AttackArea != null)
        {
            AttackArea.BodyEntered += OnBodyEntered;
            AttackArea.BodyExited += OnBodyExited;
        }
    }

    public override void _Process(double delta)
    {
        if (IsDead) return;

        if (TargetTile == null || TargetTile.IsDestroyed || !TargetTile.IsInsideTree())
        {
            UpdateTarget();
        }

        MoveTowardsTarget();
        HandleAttacks(delta);
    }

    protected virtual void MoveTowardsTarget()
    {
        if (TargetTile != null)
        {
            var direction = GlobalPosition.DirectionTo(TargetTile.GlobalPosition);
            Velocity = direction * MoveSpeed;
            LookAt(TargetTile.GlobalPosition);
            MoveAndSlide();
        }
    }

    protected virtual void HandleAttacks(double delta)
    {
        if (AttackTimer > 0)
        {
            AttackTimer -= (float)delta;
        }
        else if (CollidingTiles.Count > 0)
        {
            foreach (var tile in CollidingTiles)
            {
                if (tile != null && !tile.IsDestroyed && tile.IsInsideTree())
                {
                    TryAttackTile(tile);
                    break;
                }
            }
        }
    }

    protected virtual void UpdateTarget()
    {
        // If we have a valid target in collision, use that
        foreach (var tile in CollidingTiles)
        {
            if (tile != null && !tile.IsDestroyed && tile.IsInsideTree())
            {
                TargetTile = tile;
                return;
            }
        }

        // Otherwise find the reactor
        if (Reactor != null && !Reactor.IsDestroyed && Reactor.IsInsideTree())
        {
            TargetTile = Reactor;
        }
        else
        {
            TargetTile = null;
        }
    }

    protected virtual void TryAttackTile(BaseTile tile)
    {
        if (IsDead || tile == null || tile.IsDestroyed || !tile.IsInsideTree())
            return;

        AttackTimer = AttackCooldown;
        bool wasDestroyed = tile.TakeDamage(Damage);
        GD.Print($"Attacking {tile.Name} for {Damage} damage. Was destroyed: {wasDestroyed}");

        if (wasDestroyed)
        {
            CollidingTiles.Remove(tile);
            if (TargetTile == tile)
            {
                TargetTile = null;
            }
        }
    }

    protected virtual void OnBodyEntered(Node2D body)
    {
        if (body is BaseTile { IsDestroyed: false } tile && !body.IsInGroup("Hostile"))
        {
            GD.Print($"[Enemy] {body.Name} Entered attack range");
            CollidingTiles.Add(tile);
            if (TargetTile == null || TargetTile.IsDestroyed || !TargetTile.IsInsideTree())
            {
                TargetTile = tile;
            }

            // Attack immediately on collision
            TryAttackTile(tile);
        }
    }

    protected virtual void OnBodyExited(Node2D body)
    {
        if (body.GetParent() is BaseTile tile)
        {
            CollidingTiles.Remove(tile);
            if (TargetTile == tile)
            {
                TargetTile = null;
            }
        }
    }

    protected virtual void OnBodyEnteredDetection(Node2D body)
    {
        // Can be overridden by derived classes
    }

    protected virtual void OnBodyExitedDetection(Node2D body)
    {
        // Can be overridden by derived classes
    }

    public virtual void TakeDamage(int damage, Vector2? hitPosition = null)
    {
        if (IsDead) return;

        CurrentHealth -= damage;

        // Show damage number (optional)
        if (ShowDamageNumbers)
        {
            var damageLabel = new Label
            {
                Text = $"-{damage}",
                Position = hitPosition ?? GlobalPosition,
                ZIndex = 1000
            };

            GetTree().CurrentScene.AddChild(damageLabel);

            // Animate and remove damage number
            var tween = CreateTween();
            tween.TweenProperty(damageLabel, "position:y", damageLabel.Position.Y - 30, 0.5f);
            tween.TweenCallback(Callable.From(() => damageLabel.QueueFree())).SetDelay(0.5f);
        }

        // Visual feedback
        var originalModulate = Modulate;
        Modulate = new Color(1, 0.5f, 0.5f); // Flash red

        var tweenFlash = CreateTween();
        tweenFlash.TweenProperty(this, "modulate", originalModulate, 0.2f);
    }

    protected virtual void UpdateHealthBar()
    {
        if (HealthBar != null)
        {
            HealthBar.Value = CurrentHealthValue;
            HealthBar.Visible = CurrentHealthValue < MaxHealth; // Only show when damaged
        }
    }

    protected virtual void Die()
    {
        // Play death animation/sound
        // You can add a death animation here

        // Disable collisions and hide
        SetProcess(false);
        SetPhysicsProcess(false);
        Hide();

        // Queue free after a delay (for any death animation/sound to play)
        var timer = new Timer();
        AddChild(timer);
        timer.Timeout += () => QueueFree();
        timer.Start(0.5f);
    }
}
