using PkmnLib.Dynamic;
using PkmnLib.Dynamic.Libraries;
using PkmnLib.Static.Moves;

namespace PkmnLib.Plugin.Gen7.Libraries.Battling;

public class Gen7MiscLibrary : IMiscLibrary
{
    private readonly IMoveData _struggleData = new MoveDataImpl("struggle", new TypeIdentifier(0, "none"),
        MoveCategory.Physical, 50, 255, 255, MoveTarget.Any, 0,
        new SecondaryEffectImpl(-1, "struggle", new Dictionary<StringKey, object?>()), ["not_sketchable"]);

    /// <inheritdoc />
    public ITurnChoice ReplacementChoice(IPokemon user, byte targetSide, byte targetPosition) =>
        new MoveChoice(user, new LearnedMoveImpl(_struggleData, MoveLearnMethod.Unknown), targetSide, targetPosition);

    /// <inheritdoc />
    public bool IsReplacementChoice(ITurnChoice choice) =>
        choice is MoveChoice moveChoice && moveChoice.ChosenMove.MoveData == _struggleData;

    /// <inheritdoc />
    public TimeOfDay GetTimeOfDay()
    {
        var time = StaticHelpers.GetCurrentDateTime().LocalDateTime;
        var hour = time.Hour;
        return hour switch
        {
            >= 0 and <= 5 => TimeOfDay.Night,
            >= 6 and <= 9 => TimeOfDay.Morning,
            >= 10 and <= 16 => TimeOfDay.Day,
            17 => TimeOfDay.Evening,
            _ => TimeOfDay.Night,
        };
    }

    /// <inheritdoc />
    public bool CanFlee(IBattle battle, IFleeChoice fleeChoice)
    {
        var user = fleeChoice.User;
        var battleData = user.BattleData;
        if (battleData == null)
            return false;
        var opponentSide = battle.Sides[battleData.SideIndex == 0 ? 1 : 0];
        var opponent = opponentSide.Pokemon.FirstOrDefault(x => x is not null);
        if (opponent == null)
            return true;

        var userSpeed = user.FlatStats.Speed;
        var opponentSpeed = opponent.FlatStats.Speed;
        // If the player's active Pokémon's Speed is greater than or equal to the wild Pokémon's Speed, fleeing will
        // always succeed
        if (userSpeed >= opponentSpeed)
            return true;

        var userSide = battle.Sides[battleData.SideIndex];

        userSide.RegisterFleeAttempt();
        var fleeChance = userSpeed * 32 / (opponentSpeed / 4) + 30 * userSide.FleeAttempts;
        var random = battle.Random.GetInt(0, 256);
        return random < fleeChance;
    }
}