using System.Collections.Immutable;
using System.Text.Json;
using PkmnLib.Dynamic.Libraries.DataLoaders.Models;
using PkmnLib.Static;
using PkmnLib.Static.Libraries;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;

namespace PkmnLib.Dynamic.Libraries.DataLoaders;

/// <summary>
/// Loads item data from a JSON file.
/// </summary>
public static class ItemDataLoader
{
    /// <summary>
    /// Loads the item library from a JSON file.
    /// </summary>
    public static ItemLibrary LoadItems(Stream stream, Action<List<SerializedItem>>? onAfterLoad = null)
    {
        var library = new ItemLibrary();
        var obj = JsonSerializer.Deserialize<List<SerializedItem>>(stream, JsonOptions.DefaultOptions);
        if (obj == null)
            throw new InvalidDataException("Item data is empty.");
        onAfterLoad?.Invoke(obj);
        var items = obj.Select(DeserializeItem);
        foreach (var i in items)
            library.Add(i);
        return library;
    }

    /// <summary>
    /// Factory delegate for creating items.
    /// </summary>
    public delegate IItem ItemFactoryDelegate(SerializedItem serialized, StringKey name, ItemCategory type,
        BattleItemCategory battleType, int price, ImmutableHashSet<StringKey> flags, ISecondaryEffect? effect,
        ISecondaryEffect? battleTriggerEffect, Dictionary<StringKey, object?> additionalData);

    /// <summary>
    /// The item constructor. This is used to create items from the JSON data.
    /// </summary>
    [PublicAPI]
    public static ItemFactoryDelegate ItemConstructor { get; set; } =
        (_, name, type, battleType, price, flags, effect, battleTriggerEffect, additionalData) => new ItemImpl(name,
            type, battleType, price, flags, effect, battleTriggerEffect, additionalData);

    private static IItem DeserializeItem(SerializedItem serialized)
    {
        if (!Enum.TryParse<ItemCategory>(serialized.ItemType, true, out var itemType))
            throw new InvalidDataException($"Item type {serialized.ItemType} is not valid for item {serialized.Name}.");
        Enum.TryParse(serialized.BattleType, true, out BattleItemCategory battleType);
        var effect = serialized.Effect?.ParseEffect();
        var battleTriggerEffect = serialized.BattleEffect?.ParseEffect();
        var additionalData =
            serialized.AdditionalData?.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter()) ??
            new Dictionary<StringKey, object?>();

        return ItemConstructor(serialized, serialized.Name, itemType, battleType, serialized.Price,
            serialized.Flags.Select(x => (StringKey)x).ToImmutableHashSet(), effect, battleTriggerEffect,
            additionalData);
    }
}