using System.Collections.Immutable;
using PkmnLib.Static.Utils;

namespace PkmnLib.Static.Species;

/// <summary>
/// An ability is a passive effect in battle that is attached to a Pokémon.
/// </summary>
public interface IAbility : INamedValue
{
    /// <summary>
    /// The name of the script effect of the ability. This should refer to the name of the script that will be executed
    /// when the ability is triggered.
    /// </summary>
    StringKey? Effect { get; }

    /// <summary>
    /// The parameters for the script effect of the ability.
    /// </summary>
    IReadOnlyDictionary<StringKey, object?> Parameters { get; }

    /// <summary>
    /// Checks whether the ability has a specific flag. 
    /// </summary>
    bool HasFlag(StringKey key);

    /// <summary>
    /// Indicates whether the ability can be changed by effects such as Skill Swap or Role Play.
    /// </summary>
    bool CanBeChanged { get; }
}

/// <inheritdoc />
public class AbilityImpl : IAbility
{
    /// <inheritdoc cref="AbilityImpl" />
    public AbilityImpl(StringKey name, StringKey? effect, IReadOnlyDictionary<StringKey, object?> parameters,
        ImmutableHashSet<StringKey> flags, bool canBeChanged)
    {
        Name = name;
        Effect = effect;
        Parameters = parameters;
        Flags = flags;
        CanBeChanged = canBeChanged;
    }

    /// <inheritdoc />
    public StringKey Name { get; }

    /// <inheritdoc />
    public StringKey? Effect { get; }

    /// <inheritdoc />
    public IReadOnlyDictionary<StringKey, object?> Parameters { get; }

    /// <summary>
    /// A collection of arbitrary flags that can be used to mark the ability with specific properties.
    /// </summary>
    public ImmutableHashSet<StringKey> Flags;

    /// <inheritdoc />
    public bool HasFlag(StringKey key) => Flags.Contains(key);

    /// <inheritdoc />
    public bool CanBeChanged { get; }
}

/// <summary>
/// An ability index allows us to find an ability on a form. It combines a bool for whether the
/// ability is hidden or not, and then an index of the ability.
/// </summary>
public readonly record struct AbilityIndex
{
    /// <summary>
    /// Whether the ability we're referring to is a hidden ability.
    /// </summary>
    public required bool IsHidden { get; init; }

    /// <summary>
    /// The index of the ability.
    /// </summary>
    public required byte Index { get; init; }
}