using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Dynamic.ScriptHandling.Registry;
using PkmnLib.Static.Libraries;

namespace PkmnLib.Dynamic.Libraries;

/// <summary>
/// The dynamic library stores a static data library, as well as holding different libraries and
/// calculators that might be customized between different generations and implementations.
/// </summary>
public interface IDynamicLibrary
{
    /// <summary>
    /// The static data is the immutable storage data for this library.
    /// </summary>
    IStaticLibrary StaticLibrary { get; }

    /// <summary>
    /// The stat calculator deals with the calculation of flat and boosted stats, based on the
    /// Pokémon's attributes.
    /// </summary>
    IBattleStatCalculator StatCalculator { get; }

    /// <summary>
    /// The damage calculator deals with the calculation of things relating to damage.
    /// </summary>
    IDamageCalculator DamageCalculator { get; }

    /// <summary>
    /// The Misc Library holds minor functions that do not fall in any of the other libraries and
    /// calculators.
    /// </summary>
    IMiscLibrary MiscLibrary { get; }
    
    /// <summary>
    /// The capture library deals with the calculation of the capture rate of a Pokémon.
    /// </summary>
    ICaptureLibrary CaptureLibrary { get; }
    
    /// <summary>
    /// A holder of the script types that can be resolved by this library.
    /// </summary>
    ScriptResolver ScriptResolver { get; }
}

/// <inheritdoc />
public class DynamicLibraryImpl : IDynamicLibrary
{
    /// <summary>
    /// Initializes a new instance of the <see cref="DynamicLibraryImpl"/> class, with the given
    /// plugins and static library.
    /// </summary>
    public static IDynamicLibrary Create(IStaticLibrary staticLibrary, IEnumerable<Plugin> plugins)
    {
        var registry = new ScriptRegistry();
        foreach (var plugin in plugins.OrderBy(x => x.LoadOrder))
        {
            plugin.Register(registry);
        }
        if (registry.DamageCalculator is null)
            throw new InvalidOperationException("Damage calculator not found in plugins.");
        if (registry.BattleStatCalculator is null)
            throw new InvalidOperationException("Stat calculator not found in plugins.");
        if (registry.MiscLibrary is null)
            throw new InvalidOperationException("Misc library not found in plugins.");
        if (registry.CaptureLibrary is null)
            throw new InvalidOperationException("Capture library not found in plugins.");
        var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes);
        return new DynamicLibraryImpl(staticLibrary, registry.BattleStatCalculator,
            registry.DamageCalculator, registry.MiscLibrary, registry.CaptureLibrary, scriptResolver);
    }
    
    private DynamicLibraryImpl(IStaticLibrary staticLibrary, IBattleStatCalculator statCalculator,
        IDamageCalculator damageCalculator, IMiscLibrary miscLibrary, ICaptureLibrary captureLibrary, ScriptResolver scriptResolver)
    {
        StaticLibrary = staticLibrary;
        StatCalculator = statCalculator;
        DamageCalculator = damageCalculator;
        MiscLibrary = miscLibrary;
        ScriptResolver = scriptResolver;
        CaptureLibrary = captureLibrary;
    }

    /// <inheritdoc />
    public IStaticLibrary StaticLibrary { get; }

    /// <inheritdoc />
    public IBattleStatCalculator StatCalculator { get; }

    /// <inheritdoc />
    public IDamageCalculator DamageCalculator { get; }

    /// <inheritdoc />
    public IMiscLibrary MiscLibrary { get; }

    /// <inheritdoc />
    public ICaptureLibrary CaptureLibrary { get; }

    /// <inheritdoc />
    public ScriptResolver ScriptResolver { get; }
}