using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Abstract.Events;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Status.Effect;

namespace VOID.ScriptableObjects
{
    [Serializable]
    [CreateAssetMenu(fileName = "Status", menuName = "GAME DATA/Status", order = 0)]
    public class BasicStatus : ScriptableObject
    {
        [Title("Nested into:")]
        [ShowInInspector] public AbstractObject forObject { get; private set; }

        [Title("Display")]
        [Required] public string displayName;
        [Required] public bool hidden = false;
        [Required] public Texture2D displayIcon;
        [Required, TextArea(10,10)] public string description;

        [Title("Effects list")]
        [Required, SerializeReference] public List<BasicEffect> effects = new();

        [Title("Statuses interactions")]
        public List<BasicStatus> removes = new();

        [Title("Duration")]
        public bool permanent = false;
        [MinValue(0)] public int durationInTurns;
        [ReadOnly] public int durationLeftInTurns;
        [ReadOnly] public float durationLeft;

        public event Action OnTimeDecrease;
        
        public void Init(AbstractObject forObject, UnitObject source = null)
        {
            this.forObject = forObject;
            effects.ForEach(effect => effect.Init(forObject, this, source));
        }

        public void Apply()
        {
            if (permanent) durationInTurns = 1;
            
            // TODO: zdefiniować gdzieś ile czasu w sekundach to tura, na sztywno tutaj dodane 4 sekundy
            durationLeftInTurns = durationInTurns;
            durationLeft = durationInTurns*4f - 0.0001f;

            var otherStatuses = forObject.statusManager.GetStatuses();
            var selfRemove = false;

            foreach (var otherStatus in otherStatuses)
            {
                if (otherStatus == this) continue;
                
                // if other status removes current one
                selfRemove = selfRemove || otherStatus.removes.Any(removeStatus => removeStatus.GetType() == GetType());
                
                // if other status is removed by current one
                if (removes.Any(statusToRemove => statusToRemove.GetType() == otherStatus.GetType())) otherStatus.Cancel();
            }

            if (selfRemove)
            {
                forObject.statusManager.RemoveStatus(this);
                return;
            }
            
            forObject.basicEvents.onStatusStart.Invoke(new StatusEvent
            {
                obj = forObject,
                status = this
            });
            
            // Statuses already added via editor inspector need to be started this way
            foreach (var effect in effects)
            {
                effect.OnApply();
                forObject.basicEvents.onEffectStart.Invoke(new EffectEvent
                {
                    effect = effect
                });
            }
        }
        
        public void End()
        {
            if (permanent)
            {
                durationLeftInTurns = 1;
                // TODO: zdefiniować gdzieś ile czasu w sekundach to tura, na sztywno tutaj dodane 4 sekundy
                durationLeft += durationInTurns * 4f;
                return;
            }

            foreach (var effect in effects)
            {
                effect.OnEnd();
                forObject.basicEvents.onEffectEnd.Invoke(new EffectEvent
                {
                    effect = effect
                });
            }
            forObject.statusManager.RemoveStatus(this);
            forObject.basicEvents.onStatusEnd.Invoke(new StatusEvent
            {
                obj = forObject,
                status = this
            });
        }

        public void Cancel()
        {
            effects.ForEach(effect =>
            {
                effect.OnCancel();
                forObject.basicEvents.onEffectEnd.Invoke(new EffectEvent { effect = effect });
            });
            forObject.statusManager.RemoveStatus(this);
            forObject.basicEvents.onStatusEnd.Invoke(new StatusEvent { obj = forObject, status = this });
        }

        public void OnTurnSelfStart()
        {
            // TODO: zdefiniować gdzieś ile czasu w sekundach to tura, na sztywno tutaj dodane 4 sekundy
            durationLeftInTurns -= 1;
            durationLeft -= 4f;
            effects.ForEach(effect => effect.OnTurnSelfStart());
            OnTimeDecrease?.Invoke();
        }

        public void OnTurnSelfEnd()
        {
            effects.ForEach(effect => effect.OnTurnSelfEnd());
            if (durationLeft <= 0f) End();
        }

        public void DecreaseDurationRealTime()
        {
            // TODO: zdefiniować gdzieś ile czasu w sekundach to tura, na sztywno tutaj dodane 4 sekundy
            if (durationLeft%4f - Time.fixedDeltaTime < 0f)
            {
                effects.ForEach(effect => effect.OnTurnSelfStart());
                effects.ForEach(effect => effect.OnTurnSelfEnd());
                durationLeftInTurns -= 1;
            }
            
            OnTimeDecrease?.Invoke();

            durationLeft -= Time.fixedDeltaTime;
            
            if (durationLeft < 0f) End();
        }
    }
}
