using PkmnLib.Dynamic.AI.Explicit;
using PkmnLib.Plugin.Gen7.AI;

namespace PkmnLib.Plugin.Gen7.Scripts.Moves;

public abstract class ChangeTargetStats : Script, IScriptOnInitialize, IScriptOnSecondaryEffect
{
    private readonly Statistic _stat;
    private sbyte _amount;

    protected ChangeTargetStats(Statistic stat)
    {
        _stat = stat;
    }

    /// <inheritdoc />
    public void OnInitialize(IReadOnlyDictionary<StringKey, object?>? parameters)
    {
        if (parameters == null)
        {
            throw new ArgumentNullException(nameof(parameters));
        }

        if (!parameters.TryGetValue("amount", out var amount) || amount == null)
        {
            throw new ArgumentException("Parameter 'amount' is required.");
        }

        _amount = (sbyte)(int)amount;
    }

    /// <inheritdoc />
    public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
    {
        target.ChangeStatBoost(_stat, _amount, target == move.User, false);
    }

    protected static void GetMoveEffectScore(MoveOption option, Statistic stat, ref int score)
    {
        if (option.Move.Move.SecondaryEffect == null ||
            !option.Move.Move.SecondaryEffect.Parameters.TryGetValue("amount", out var amountObj) ||
            amountObj is not int amount)
        {
            return;
        }
        var statisticSet = new StatBoostStatisticSet();
        statisticSet.SetStatistic(stat, (sbyte)amount);
        score = AIHelperFunctions.GetScoreForTargetStatDrop(score, option.Move, option.Move.User, statisticSet);
    }
}

[Script(ScriptCategory.Move, "change_target_attack")]
public class ChangeTargetAttack : ChangeTargetStats
{
    public ChangeTargetAttack() : base(Statistic.Attack)
    {
    }

    [AIMoveScoreFunction]
    public static void AIMoveEffectScore(IExplicitAI ai, MoveOption option, ref int score) =>
        GetMoveEffectScore(option, Statistic.Attack, ref score);
}

[Script(ScriptCategory.Move, "change_target_defense")]
public class ChangeTargetDefense : ChangeTargetStats
{
    public ChangeTargetDefense() : base(Statistic.Defense)
    {
    }

    [AIMoveScoreFunction]
    public static void AIMoveEffectScore(IExplicitAI ai, MoveOption option, ref int score) =>
        GetMoveEffectScore(option, Statistic.Defense, ref score);
}

[Script(ScriptCategory.Move, "change_target_special_attack")]
public class ChangeTargetSpecialAttack : ChangeTargetStats
{
    public ChangeTargetSpecialAttack() : base(Statistic.SpecialAttack)
    {
    }

    [AIMoveScoreFunction]
    public static void AIMoveEffectScore(IExplicitAI ai, MoveOption option, ref int score) =>
        GetMoveEffectScore(option, Statistic.SpecialAttack, ref score);
}

[Script(ScriptCategory.Move, "change_target_special_defense")]
public class ChangeTargetSpecialDefense : ChangeTargetStats
{
    public ChangeTargetSpecialDefense() : base(Statistic.SpecialDefense)
    {
    }

    [AIMoveScoreFunction]
    public static void AIMoveEffectScore(IExplicitAI ai, MoveOption option, ref int score) =>
        GetMoveEffectScore(option, Statistic.SpecialDefense, ref score);
}

[Script(ScriptCategory.Move, "change_target_speed")]
public class ChangeTargetSpeed : ChangeTargetStats
{
    public ChangeTargetSpeed() : base(Statistic.Speed)
    {
    }

    [AIMoveScoreFunction]
    public static void AIMoveEffectScore(IExplicitAI ai, MoveOption option, ref int score) =>
        GetMoveEffectScore(option, Statistic.Speed, ref score);
}

[Script(ScriptCategory.Move, "change_target_accuracy")]
public class ChangeTargetAccuracy : ChangeTargetStats
{
    public ChangeTargetAccuracy() : base(Statistic.Accuracy)
    {
    }

    [AIMoveScoreFunction]
    public static void AIMoveEffectScore(IExplicitAI ai, MoveOption option, ref int score) =>
        GetMoveEffectScore(option, Statistic.Accuracy, ref score);
}

[Script(ScriptCategory.Move, "change_target_evasion")]
public class ChangeTargetEvasion : ChangeTargetStats
{
    public ChangeTargetEvasion() : base(Statistic.Evasion)
    {
    }

    [AIMoveScoreFunction]
    public static void AIMoveEffectScore(IExplicitAI ai, MoveOption option, ref int score) =>
        GetMoveEffectScore(option, Statistic.Evasion, ref score);
}