#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using TagFighter.Effects.ResourceLocationAccessors.ContextRegisters;
using TagFighter.Effects.Steps;
using TagFighter.Resources;
using UnityEngine;

namespace TagFighter
{
    public struct EffectOrigin
    {
        public Transform Transform;
        public EffectOrigin(Transform transform) {
            Transform = transform;
        }
    }
    public struct EffectTarget
    {
        public Transform Transform;
        public EffectTarget(Transform transform) {
            Transform = transform;
        }

    }
}

namespace TagFighter.Effects
{
    public class ReleaseMultiplier
    {
        public Type? MatchingAoe { get; set; }
        public float MatchingAoeReleaseMultiplier { get; set; } = 1f;
        public float NonMatchingAoeReleaseMultiplier { get; set; } = 1f;
    }

    public record RegistryKey
    {
        public Type ContextRegisterType;
        public Type ResourceType;
        public RegistryKey(Type contextRegisterType, Type resourceType) {
            ContextRegisterType = contextRegisterType;
            ResourceType = resourceType;
        }
    }

    public class EffectContext
    {
        Dictionary<RegistryKey, IUnit> _resourceRegistry = new();

        public EffectOrigin Caster { get; set; }
        public EffectTarget EffectLocation { get; set; }

        public IAreaOfEffect AreaOfEffect;

        public List<IDelayedEffect> EffectsToTrigger { get; set; } = new();
        public List<OutputStep<bool>> EffectStepsToTrigger { get; set; } = new();

        public IEffectSystem EffectSystem { get; private set; }

        public ReleaseMultiplier ReleaseMultiplier { get; set; }

        public EffectContext(EffectOrigin caster, EffectTarget effectLocation) {
            EffectSystem = SystemsHandler.EffectSystem;
            ReleaseMultiplier = new();
            Caster = caster;
            EffectLocation = effectLocation;
            AreaOfEffect = new SingleTarget();
        }

        public Unit<TUnit> GetResource<TResource, TUnit, TContextRegister>()
            where TResource : Resource<TUnit>
            where TUnit : IUnitType
            where TContextRegister : IContextRegister {
            var key = new RegistryKey(typeof(TContextRegister), typeof(TResource));
            var value = (Unit<TUnit>)_resourceRegistry.GetValueOrDefault(key, (Unit<TUnit>)0);
            Debug.Log($"{nameof(GetResource)} ({typeof(TResource).Name},{typeof(TContextRegister).Name}) = {value}");
            return value;
        }

        public Unit<TUnit> SetResource<TResource, TUnit, TContextRegister>(Unit<TUnit> value)
            where TResource : Resource<TUnit>
            where TUnit : IUnitType
            where TContextRegister : IContextRegister {
            var key = new RegistryKey(typeof(TContextRegister), typeof(TResource));
            var newValue = (Unit<TUnit>)(_resourceRegistry[key] = value);
            Debug.Log($"{nameof(SetResource)} ({typeof(TResource).Name},{typeof(TContextRegister).Name})  = {newValue}");
            return newValue;
        }

        public IEnumerable<(Type, IUnit)> GetAllResourcesInRegister<TContextRegister>() where TContextRegister : IContextRegister {
            return _resourceRegistry.Where(x => x.Key.ContextRegisterType == typeof(TContextRegister)).Select(x => (x.Key.ResourceType, x.Value));
        }

        public IEnumerable<Transform> GetAffectedUnits() {
            var affectedUnits = AreaOfEffect.GetAffectedUnits(Caster, EffectLocation);

            return affectedUnits;
        }

        void ResetEffects() {
            EffectsToTrigger.Clear();
            EffectStepsToTrigger.Clear();
        }

        public void Reset() {
            ResetEffects();
        }
    }
}