using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Static;

namespace PkmnLib.Dynamic.Models.Choices;

/// <summary>
/// A choice to use an item
/// </summary>
public interface IItemChoice : ITurnChoice
{
    /// <summary>
    /// The item that is used.
    /// </summary>
    IItem Item { get; }

    /// <summary>
    /// The target Pokémon of the item, if any.
    /// </summary>
    IPokemon? GetTargetPokemon(IBattle battle);
}

/// <inheritdoc cref="IItemChoice"/>
public class ItemChoice : TurnChoice, IItemChoice
{
    /// <inheritdoc cref="ItemChoice"/>
    private ItemChoice(IPokemon user, IItem item, byte? targetSide, byte? targetPosition, IPokemon? targetPokemon) :
        base(user)
    {
        Item = item;
        TargetSide = targetSide;
        TargetPosition = targetPosition;
        TargetPokemon = targetPokemon;
    }

    public static ItemChoice CreateWithoutTarget(IPokemon user, IItem item) =>
        new(user, item, null, null, null);

    public static ItemChoice CreateForOpponent(IPokemon user, IItem item, byte targetSide, byte targetPosition) =>
        new(user, item, targetSide, targetPosition, null);

    public static ItemChoice CreateForPartyMember(IPokemon user, IItem item, IPokemon targetPokemon) =>
        new(user, item, null, null, targetPokemon);

    /// <summary>
    /// The item that is used.
    /// </summary>
    public IItem Item { get; }

    /// <inheritdoc />
    public IPokemon? GetTargetPokemon(IBattle battle)
    {
        if (TargetPokemon != null)
            return TargetPokemon;

        if (!TargetSide.HasValue || !TargetPosition.HasValue)
            return null;
        var side = battle.Sides[TargetSide.Value];
        return side.Pokemon[TargetPosition.Value];
    }

    /// <summary>
    /// The target side of the item, if any.
    /// </summary>
    private byte? TargetSide { get; }

    /// <summary>
    /// The target position of the item, if any.
    /// </summary>
    private byte? TargetPosition { get; }

    /// <summary>
    /// The target Pokémon of the item, if any. This is used for party members.
    /// </summary>
    private IPokemon? TargetPokemon { get; }

    /// <inheritdoc />
    public override int ScriptCount => User.ScriptCount;

    /// <inheritdoc />
    public override void GetOwnScripts(List<IEnumerable<ScriptContainer>> scripts)
    {
    }

    /// <inheritdoc />
    public override void CollectScripts(List<IEnumerable<ScriptContainer>> scripts)
    {
        User.CollectScripts(scripts);
    }

    /// <inheritdoc />
    public override string ToString() =>
        $"ItemChoice: User: {User}, Item: {Item.Name}, TargetSide: {TargetSide}, TargetPosition: {TargetPosition}";

    protected bool Equals(ItemChoice other) =>
        other.User == User && other.Item.Equals(Item) && other.TargetSide == TargetSide &&
        other.TargetPosition == TargetPosition;

    /// <inheritdoc />
    public override bool Equals(object? obj)
    {
        if (obj is null)
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (obj is not ItemChoice other)
            return false;
        return Equals(other);
    }

    /// <inheritdoc />
    public override int GetHashCode() =>
        HashCode.Combine(User, Item, TargetSide, TargetPosition);
}