using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text.Json;
using PkmnLib.Dataloader.Models;
using PkmnLib.Static;
using PkmnLib.Static.Libraries;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;

namespace PkmnLib.Dataloader;

public static class MoveDataLoader
{
    public static MoveLibrary LoadMoves(Stream stream, TypeLibrary typeLibrary)
    {
        var library = new MoveLibrary();
        var objects = JsonSerializer.Deserialize<SerializedMoveDataWrapper>(stream, JsonOptions.DefaultOptions);
        if (objects == null)
            throw new InvalidDataException("Move data is empty.");
        var moves = objects.Data.Select(x => DeserializeMove(x, typeLibrary));
        foreach (var m in moves)
            library.Add(m);
        return library;
    }

    public static
        Func<SerializedMove, StringKey, TypeIdentifier, MoveCategory, byte, byte, byte, MoveTarget, sbyte,
            ISecondaryEffect?, IEnumerable<StringKey>, MoveDataImpl> MoveConstructor =
        (serialized, name, moveType, category, basePower, accuracy, baseUsages, target, priority, secondaryEffect,
            flags) => new MoveDataImpl(name, moveType, category, basePower, accuracy, baseUsages, target, priority,
            secondaryEffect, flags);

    private static MoveDataImpl DeserializeMove(SerializedMove serialized, TypeLibrary typeLibrary)
    {
        var type = serialized.Type;
        var power = serialized.Power;
        var pp = serialized.PP;
        var accuracy = serialized.Accuracy;
        var priority = serialized.Priority;
        var target = serialized.Target;
        var category = serialized.Category;
        var flags = serialized.Flags;
        var effect = serialized.Effect;

        if (target.Equals("self", StringComparison.InvariantCultureIgnoreCase))
            target = "selfUse";

        if (!typeLibrary.TryGetTypeIdentifier(type, out var typeIdentifier))
            throw new InvalidDataException($"Type {type} is not a valid type.");
        if (!Enum.TryParse<MoveCategory>(category, true, out var categoryEnum))
            throw new InvalidDataException($"Category {category} is not a valid category.");
        if (!Enum.TryParse<MoveTarget>(target, true, out var targetEnum))
            throw new InvalidDataException($"Target {target} is not a valid target.");
        var secondaryEffect = effect.ParseEffect();

        var move = MoveConstructor(serialized, serialized.Name, typeIdentifier, categoryEnum, power, accuracy, pp,
            targetEnum, priority, secondaryEffect, flags.Select(x => (StringKey)x).ToImmutableHashSet());
        return move;
    }
}