using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Dict.Ability;
using VOID.Generic.DynamicValue;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Projectile;
using VOID.Generic.Requirement;
using VOID.Generic.Status.Effect;
using VOID.ScriptableObjects.Animations;

namespace VOID.ScriptableObjects.Abilities
{
    [CreateAssetMenu(menuName = "GAME DATA/Zdolności/Ability")]
    public class BasicAbility : ScriptableObject
    {
        [Space(40)]
        [Title("Display UI")]
        [Required] public string displayName;
        [Required] public Texture2D displayIcon;
        [Required] public AbilityQuality quality;
        public DynamicText displayDescription;

        [Space(40)]
        [Title("Resource costs")]
        public DynamicFormula actionPointCost;
        public DynamicFormula movePointCost;

        [Space(40)]
        [Title("Requirements")]
        [SerializeReference] public List<IUnitRequirement> requirements = new();
        
        [Space(40)]
        [Title("Casting")]
        [Required] public AbilityCastingType castingType;
        [ShowIf(nameof(ShowAimingType))] [Required] public AbilityAimingType aimingType;
        [ShowIf(nameof(ShowAimingCorrectionType))] [Required] public AbilityAimingCorrectionType aimingCorrectionType;
        [InfoBox("Amount of targets for projectiles")]
        [Required] [MinValue(0)] [ShowIf(nameof(ShowCastUsages))] public int castUsages = 1;
        
        [Space(40)]
        [Title("Range")]
        [ShowIf(nameof(ShowIsRange))] [Required] public bool isRanged;
        [ShowIf(nameof(ShowRange)), SerializeField] private DynamicFormula _range;
        public float range => ShowRange() ? _range : 0.25f;

        [Space(40)]
        [Title("Animation")]
        [ShowIf(nameof(ShowAbilityAnimationList))] [Required] public ActionAnimationList abilityAnimationList;
        [ShowIf(nameof(ShowAbilityLoopAnimationList))] [Required] public ActionLoopAnimationList abilityLoopAnimationList;
        [ShowIf(nameof(ShowCastingAnimationList))] public ActionLoopAnimationList castingAnimationList;

        [Space(40)]
        [Title("Effects")]
        public AbilityExecutionMoment applyOnCasterMoment;
        [SerializeReference] [ShowIf(nameof(ShowApplyOnCaster))] [TypeSelectorSettings(ShowCategories = true, ShowNoneItem = false)]
        public List<BasicEffect> applyOnCaster = new();
        [SerializeReference] [ShowIf(nameof(ShowApplyOnTarget))] [TypeSelectorSettings(ShowCategories = true, ShowNoneItem = false)]
        public List<BasicEffect> applyOnTarget = new();

        [Space(40)]
        [Title("Tagging")]
        public AbilityPurposeFlag abilityPurpose;
        
        [Space(40)]
        [Title("Projectile")]
        [InfoBox("IMPORTANT!\nSo far only first step of projectile is fully supported!")]
        [ShowIf(nameof(ShowProjectileSteps))] [RequiredListLength(1)] public ProjectileStep[] projectileSteps;
        [InfoBox("If no projectile selected then default empty one will be used!")]
        [ShowIf(nameof(ShowProjectile))] public ProjectileObject projectile;

        public void OnAddedToUnit(UnitObject unitObject)
        {
            displayDescription.AddSource(unitObject);
            actionPointCost.AddSource(unitObject);
            movePointCost.AddSource(unitObject);
            _range.AddSource(unitObject);
        }

        private void OnValidate()
        {
            // If properties are hiden then set them to default values
            if (!ShowAimingType()) aimingType = AbilityAimingType.Blank;
            if (!ShowAimingCorrectionType()) aimingCorrectionType = AbilityAimingCorrectionType.None;
            if (aimingType is AbilityAimingType.Blank) castUsages = 0;
            if (aimingType is AbilityAimingType.Self) castUsages = 1;
            if (!ShowIsRange()) isRanged = false;
            // if (!ShowRange()) range = 0.25f;
            if (!ShowCastingAnimationList()) castingAnimationList = null;
            if (!ShowAbilityAnimationList()) abilityAnimationList = null;
            if (!ShowAbilityLoopAnimationList()) abilityLoopAnimationList = null;
            if (!ShowApplyOnCaster()) applyOnCaster = new List<BasicEffect>();
            if (!ShowApplyOnTarget()) applyOnTarget = new List<BasicEffect>();
            if (!ShowProjectileSteps()) projectileSteps = Array.Empty<ProjectileStep>();
            if (!ShowProjectile()) projectile = null;
        }

        private bool ShowAimingType() => castingType is not AbilityCastingType.Instant and not AbilityCastingType.InstantCasting;
        private bool ShowAimingCorrectionType() => aimingType is AbilityAimingType.Target;
        private bool ShowCastUsages() => aimingType is not AbilityAimingType.Blank and not AbilityAimingType.Self;
        private bool ShowIsRange() => castUsages > 0;
        private bool ShowRange() => ShowIsRange() && isRanged;
        private bool ShowCastingAnimationList() => castingType is not AbilityCastingType.Instant and not AbilityCastingType.InstantCasting;
        private bool ShowAbilityAnimationList() => castingType is not AbilityCastingType.Instant and not AbilityCastingType.Channel;
        private bool ShowAbilityLoopAnimationList() => castingType is AbilityCastingType.Channel;
        private bool ShowApplyOnCaster() => applyOnCasterMoment is not AbilityExecutionMoment.None;
        private bool ShowApplyOnTarget() => castUsages > 0;
        private bool ShowProjectileSteps() => castUsages > 0;
        private bool ShowProjectile() => castUsages > 0;
    }
}