using System;
using System.Linq;
using Cirno.Scripts.Components.FSM;
using Cirno.Scripts.Enums;
using Cirno.Scripts.Resources;
using Godot;
using Godot.Collections;

namespace Cirno.Scripts.Components.Actors;

public partial class PlayerDamageReceiver : Area2D
{
    [Export]
    public bool Enabled { get; set; } = false;
    [Export]
    public bool Invulnerable { get; private set; } = false;
    [Export] public BulletOwner BulletGroup { get; set; } = BulletOwner.Player;

    [ExportCategory("Extensions")]
    [Export] public StringName HealthExtendName { get; private set; } = "HEALTH_EXTEND";
    [Export] public StringName ShieldExtendName { get; private set; } = "SHIELD_EXTEND";

    [Export] public float BaseHealth { get; private set; } = 32f;
    [Export] public float BaseShield { get; private set; } = 32f;
    [Export] public float BaseMotivation { get; private set; } = 100f;
    
    [Export] public float HealthExtendAmount { get; private set; } = 4f;
    [Export] public float ShieldExtendAmount { get; private set; } = 4f;
    
    [ExportCategory("Providers")]
    [Export]
    private ActorResourceProvider _healthProvider;
    [Export]
    private ActorResourceProvider _shieldProvider;
    [Export] private ActorResourceProvider _motivationProvider;
    
    [ExportCategory("Damage Types")]
    [Export] public StringName AcidGroupName { get; private set; } = "Acid";
    
    [Export] public Array<DamageResistance> ShieldDamageResistances { get; set; } = [];
    [Export] public Array<DamageResistance> HealthDamageResistances { get; set; } = [];
    
    [Signal]
    public delegate void HealthChangedEventHandler(float newValue, float maxValue);
    [Signal]
    public delegate void HealthDecreasedEventHandler(float value, float newValue, float maxValue);
    
    [Signal]
    public delegate void ShieldChangedEventHandler(float newValue, float maxValue);
    [Signal]
    public delegate void ShieldDecreasedEventHandler(float value, float newValue, float maxValue);
    
    [Signal]
    public delegate void DeathEventHandler();
    
    public float CurrentHealth
    {
        get => _healthProvider.CurrentResource;
        set => _healthProvider.CurrentResource = value;
    }

    public float CurrentShield
    {
        get => _shieldProvider.CurrentResource;
        set => _shieldProvider.CurrentResource = value;
    }
    
    public float CurrentMotivation
    {
        get => _motivationProvider.CurrentResource;
        set => _motivationProvider.CurrentResource = value;
    }

    private IStateMachine<PlayerState, CharacterBody2D> _stateMachine;
    
    public void Init(IStateMachine<PlayerState, CharacterBody2D> machine)
    {
        _stateMachine = machine;
        
        Invulnerable = GlobalState.Instance.SessionSettings.GodMode;
        
        _healthProvider.ResourceChanged += ((value, maxValue) =>
        {
            //if (!Enabled) return;
            Hud.Instance?.UpdateHealth(value, maxValue);
            EmitSignal(SignalName.HealthChanged, value, maxValue);
        });

        _healthProvider.ResourceDecreased += (value, newValue, maxValue) =>
        {
            EmitSignal(SignalName.HealthDecreased, value, newValue, maxValue);
        };
        
        _shieldProvider.ResourceDecreased += (value, newValue, maxValue) =>
        {
            EmitSignal(SignalName.ShieldDecreased, value, newValue, maxValue);
        };
        
        _shieldProvider.ResourceChanged += ((value, maxValue) =>
        {
            //if (!Enabled) return;
            Hud.Instance?.UpdateShield(value, maxValue);
            EmitSignal(SignalName.ShieldChanged, value, maxValue);
        });

        _healthProvider.ResourceDepleted += () =>
        {
            //if (!Enabled) return;
            EmitSignal(SignalName.Death);
        };
        
        // Set max resources based on inventory
        SetMaxResources();

        InventoryManager.Instance.ItemAdded += (item, amount) =>
        {
            if (item.ItemKey != HealthExtendName && item.ItemKey != ShieldExtendName) return;

            SetMaxResources();
        };

        _motivationProvider.ResourceChanged += (value, maxValue) =>
        {
            Hud.Instance?.UpdateMotivation(value, maxValue);
        };
    }

    private void SetMaxResources()
    {
        var healthExtends = InventoryManager.Instance.GetItemCount(HealthExtendName);
        
        var shieldExtends = InventoryManager.Instance.GetItemCount(ShieldExtendName);

        _healthProvider.MaxResource = BaseHealth + (healthExtends * HealthExtendAmount);
        
        _shieldProvider.MaxResource = BaseShield + (shieldExtends * ShieldExtendAmount);

        _motivationProvider.CurrentResource = BaseMotivation;
    }

    public void RefillHealth()
    {
        _healthProvider.FillResource();
    }

    public void RefillShield()
    {
        _shieldProvider.FillResource();
    }
    
    private void _on_damage_hitbox_area_entered(Area2D area)
    {
        if (!Enabled) return;
        if (Invulnerable) return;
        if (area.IsInGroup(AcidGroupName))
        {
            // Handle acid death
            AcidDeath();
            return;
        }
        if (area is not Bullet bullet || bullet.BulletOwner == BulletGroup) return;
        this.Hit(bullet.Damage, bullet.DamageType);
        bullet.RequestCollisionDestruction();
    }

    private void AcidDeath()
    {
        if (!Enabled) return;
        GD.Print("Acid death");
        _stateMachine.SetState(PlayerState.Drowning);
        //_healthProvider.CurrentResource = 0;
    }

    private void ApplyDamageToHealth(float damage, DamageType type = DamageType.Neutral)
    {
        if (HealthDamageResistances.Where(x => x.DamageType == type)
                .Any(x => x.Attribute is DamageAttribute.Skip)) return;
        
        var dmg = HealthDamageResistances.Aggregate(damage, (current, resistance) => current * resistance.CalculateDamage(current, type));
        
        CurrentHealth -= dmg;
    }
    public void Hit(float damage, DamageType type = DamageType.Neutral)
    {
        if (!Enabled) return;

        // Change value based on difficulty
        float difficultyReducedDmg = damage / GlobalState.Instance.SessionSettings.DifficultyDamageMultiplier;

        // Check if the shield is empty or damage has skip attributes
        if (CurrentShield <= 0 || ShieldDamageResistances.Where(x => x.DamageType == type).Any(x => x.Attribute is DamageAttribute.Skip))
        {
            // do not apply, go to health
            ApplyDamageToHealth(difficultyReducedDmg, type);
        }
        else
        {
            var shieldDmg = ShieldDamageResistances.Aggregate(difficultyReducedDmg, (current, resistance) => current * resistance.CalculateDamage(current, type));
            
            // apply and get remainder
            var remainder = CurrentShield - shieldDmg;
            CurrentShield = remainder; // Let the resource's self-balancing take care of any remainders

            if (remainder < 0)
            {
                // Apply remainder to health
                ApplyDamageToHealth(-remainder, type);
            }
        }

        // return;
        
        // if (CurrentShield > 0 && type is not DamageType.Explosive or DamageType.Acid)
        // {
        //     // Reduce shield
        //     //PlayShieldAnimation(); // Let this be handled by event
        //     CurrentShield -= damage;
        //     if (CurrentShield < 0)
        //     {
        //         CurrentHealth -= Math.Abs(CurrentShield);
        //         CurrentShield = 0;
        //     }
        // }
        // else
        // {
        //     if (type is DamageType.Fire)
        //     {
        //         CurrentHealth -= damage * 2;
        //     }
        //     else
        //     {
        //         CurrentHealth -= damage;
        //     }
        //
        //     //Blink(); // Let this be handled by event
        // }
        //
        // if (!(CurrentHealth <= 0)) return;
    }
}