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

namespace PkmnLib.Dynamic.Libraries.DataLoaders;

/// <summary>
/// Loads ability data from a JSON file.
/// </summary>
public static class AbilityDataLoader
{
    /// <summary>
    /// Deserializes a typed JSON object containing ability data into a dictionary of abilities.
    /// </summary>
    public static Dictionary<string, SerializedAbility> LoadAbilitiesData(Stream stream)
    {
        var obj = JsonSerializer.Deserialize<JsonObject>(stream, JsonOptions.DefaultOptions);
        if (obj == null)
            throw new InvalidDataException("Ability data is empty.");
        obj.Remove("$schema");
        var cleanedString = obj.ToJsonString();

        var objects =
            JsonSerializer.Deserialize<Dictionary<string, SerializedAbility>>(cleanedString,
                JsonOptions.DefaultOptions);
        if (objects == null)
            throw new InvalidDataException("Ability data is empty.");
        return objects;
    }

    /// <summary>
    /// Loads the ability library from a JSON file.
    /// </summary>
    public static AbilityLibrary LoadAbilities(Stream stream,
        Action<Dictionary<string, SerializedAbility>>? action = null)
    {
        var library = new AbilityLibrary();
        var objects = LoadAbilitiesData(stream);
        if (objects == null)
            throw new InvalidDataException("Ability data is empty.");
        action?.Invoke(objects);

        var abilities = objects.Select(x => DeserializeAbility(x.Key, x.Value));
        foreach (var a in abilities)
            library.Add(a);
        return library;
    }

    private static AbilityImpl DeserializeAbility(string name, SerializedAbility serialized)
    {
        var effect = serialized.Effect;
        var parameters = serialized.Parameters.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter());

        var effectName = string.IsNullOrWhiteSpace(effect) ? (StringKey?)null : new StringKey(effect);

        var flags = serialized.Flags.Select(x => new StringKey(x)).ToImmutableHashSet();

        var ability = new AbilityImpl(name, effectName, parameters, flags, serialized.CanBeChanged ?? true);
        return ability;
    }
}