using PkmnLib.Dynamic.Models;
using PkmnLib.Static.Utils;

namespace PkmnLib.Dynamic.AI.Explicit;

/// <summary>
/// An option where a move is used against a target
/// </summary>
public record struct MoveOption(AIMoveState Move, IBattle Battle, IPokemon? Target, uint EstimatedDamage = 0);

/// <summary>
/// A function that takes an explicit AI and a move option and returns a boolean value.
/// </summary>
public delegate bool AIBoolHandler(IExplicitAI ai, MoveOption option);

/// <summary>
/// A function for returning whether a Pokemon should switch.
/// </summary>
public delegate bool AISwitchBoolHandler(IExplicitAI ai, IPokemon pokemon, IBattle battle,
    IReadOnlyList<IPokemon> reserves);

/// <summary>
/// A function for returning the base power of a move.
/// </summary>
public delegate void AIMoveBasePowerHandler(IExplicitAI ai, MoveOption option, ref int score);

/// <summary>
/// A function for returning a score
/// </summary>
public delegate void AIScoreMoveHandler(IExplicitAI ai, MoveOption option, ref int score);

/// <summary>
/// Explicit AI Handlers are a library that allow plugins to explicitly indicate AI preferences.
/// </summary>
public interface IReadOnlyExplicitAIHandlers
{
    /// <summary>
    /// A list of checks to determine if a move will fail.
    /// </summary>
    IReadOnlyDictionary<StringKey, AIBoolHandler> MoveFailureCheck { get; }

    /// <summary>
    /// Checks if a move will fail based on the provided function code and options.
    /// </summary>
    bool MoveWillFail(IExplicitAI ai, StringKey functionCode, MoveOption option);

    /// <summary>
    /// A list of checks to determine if a move will fail against a target.
    /// </summary>
    IReadOnlyDictionary<StringKey, AIBoolHandler> MoveFailureAgainstTargetCheck { get; }

    /// <summary>
    /// Checks if a move will fail against a target based on the provided function code and options.
    /// </summary>
    bool MoveWillFailAgainstTarget(IExplicitAI ai, StringKey functionCode, MoveOption option);

    /// <summary>
    /// A list of handlers to apply scores for move effects.
    /// </summary>
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> MoveEffectScore { get; }

    /// <summary>
    /// Applies the score for a move effect based on the provided name and options.
    /// </summary>
    void ApplyMoveEffectScore(IExplicitAI ai, StringKey name, MoveOption option, ref int score);

    /// <summary>
    /// A list of handlers to apply scores for move effects against a target.
    /// </summary>
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> MoveEffectAgainstTargetScore { get; }

    /// <summary>
    /// Applies the score for a move effect against a target based on the provided name and options.
    /// </summary>
    void ApplyMoveEffectAgainstTargetScore(IExplicitAI ai, StringKey name, MoveOption option, ref int score);

    /// <summary>
    /// A list of handlers to determine the base power of a move.
    /// </summary>
    IReadOnlyDictionary<StringKey, AIMoveBasePowerHandler> MoveBasePower { get; }

    /// <summary>
    /// Applies the base power for a move based on the provided name and options.
    /// </summary>
    void GetBasePower(IExplicitAI ai, StringKey name, MoveOption option, ref int power);

    /// <summary>
    /// A list of handlers to apply scores for general move effectiveness.
    /// </summary>
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> GeneralMoveScore { get; }

    /// <summary>
    /// Applies the score for a general move based on the provided option.
    /// </summary>
    void ApplyGenerateMoveScoreModifiers(IExplicitAI ai, MoveOption option, ref int score);

    /// <summary>
    /// A list of handlers to apply scores for general move effectiveness against a target.
    /// </summary>
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> GeneralMoveAgainstTargetScore { get; }

    /// <summary>
    /// Applies the score for a general move against a target based on the provided option.
    /// </summary>
    void ApplyGenerateMoveAgainstTargetScoreModifiers(IExplicitAI ai, MoveOption option, ref int score);

    /// <summary>
    /// Functions that indicate whether a Pokemon should switch
    /// </summary>
    IReadOnlyDictionary<StringKey, AISwitchBoolHandler> ShouldSwitchFunctions { get; }

    /// <summary>
    /// Indicates whether a Pokemon should switch into another Pokemon
    /// </summary>
    bool ShouldSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList<IPokemon> reserves);

    /// <summary>
    /// Functions that indicate whether a Pokemon should NOT switch
    /// </summary>
    IReadOnlyDictionary<StringKey, AISwitchBoolHandler> ShouldNotSwitchFunctions { get; }

    /// <summary>
    /// Indicates whether a Pokemon should NOT switch into another Pokemon
    /// </summary>
    bool ShouldNotSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList<IPokemon> reserves);

    /// <summary>
    /// Scores abilities
    /// </summary>
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> AbilityRanking { get; }
}

/// <inheritdoc />
public class ExplicitAIHandlers : IReadOnlyExplicitAIHandlers
{
    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AIBoolHandler> IReadOnlyExplicitAIHandlers.MoveFailureCheck => MoveFailureCheck;

    /// <summary>
    /// Checks whether a move will fail
    /// </summary>
    public FunctionHandlerDictionary<AIBoolHandler> MoveFailureCheck { get; } = new();

    /// <inheritdoc />
    public bool MoveWillFail(IExplicitAI ai, StringKey functionCode, MoveOption option) =>
        MoveFailureCheck.TryGetValue(functionCode, out var handler) && handler(ai, option);

    /// <summary>
    /// Checks whether a move will fail against a target
    /// </summary>
    public FunctionHandlerDictionary<AIBoolHandler> MoveFailureAgainstTargetCheck { get; } = new();

    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AIBoolHandler> IReadOnlyExplicitAIHandlers.MoveFailureAgainstTargetCheck =>
        MoveFailureAgainstTargetCheck;

    /// <inheritdoc />
    public bool MoveWillFailAgainstTarget(IExplicitAI ai, StringKey functionCode, MoveOption option) =>
        MoveFailureAgainstTargetCheck.TryGetValue(functionCode, out var handler) && handler(ai, option);

    /// <summary>
    /// Functions to score a move
    /// </summary>
    public FunctionHandlerDictionary<AIScoreMoveHandler> MoveEffectScore { get; } = [];

    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> IReadOnlyExplicitAIHandlers.MoveEffectScore =>
        MoveEffectScore;

    /// <summary>
    /// Functions to score a move against a target
    /// </summary>
    public FunctionHandlerDictionary<AIScoreMoveHandler> MoveEffectAgainstTargetScore = [];

    /// <inheritdoc />
    public void ApplyMoveEffectScore(IExplicitAI ai, StringKey name, MoveOption option, ref int score)
    {
        if (MoveEffectScore.TryGetValue(name, out var handler))
            handler(ai, option, ref score);
    }

    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> IReadOnlyExplicitAIHandlers.MoveEffectAgainstTargetScore =>
        MoveEffectAgainstTargetScore;

    /// <summary>
    /// Functions that indicate a moves base power
    /// </summary>
    public FunctionHandlerDictionary<AIMoveBasePowerHandler> MoveBasePower = [];

    /// <inheritdoc />
    public void ApplyMoveEffectAgainstTargetScore(IExplicitAI ai, StringKey name, MoveOption option, ref int score)
    {
        if (MoveEffectAgainstTargetScore.TryGetValue(name, out var handler))
            handler(ai, option, ref score);
    }

    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AIMoveBasePowerHandler> IReadOnlyExplicitAIHandlers.MoveBasePower =>
        MoveBasePower;

    /// <summary>
    /// Functions that indicate a moves score
    /// </summary>
    public FunctionHandlerDictionary<AIScoreMoveHandler> GeneralMoveScore = [];

    /// <inheritdoc />
    public void GetBasePower(IExplicitAI ai, StringKey name, MoveOption option, ref int power)
    {
        if (MoveBasePower.TryGetValue(name, out var handler))
            handler(ai, option, ref power);
    }

    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> IReadOnlyExplicitAIHandlers.GeneralMoveScore =>
        GeneralMoveScore;

    public FunctionHandlerDictionary<AIScoreMoveHandler> GeneralMoveAgainstTargetScore = [];

    /// <inheritdoc />
    public void ApplyGenerateMoveScoreModifiers(IExplicitAI ai, MoveOption option, ref int score)
    {
        foreach (var (_, handler) in GeneralMoveScore)
        {
            handler(ai, option, ref score);
        }
    }

    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> IReadOnlyExplicitAIHandlers.GeneralMoveAgainstTargetScore =>
        GeneralMoveAgainstTargetScore;

    /// <inheritdoc />
    public void ApplyGenerateMoveAgainstTargetScoreModifiers(IExplicitAI ai, MoveOption option, ref int score)
    {
        foreach (var (_, handler) in GeneralMoveAgainstTargetScore)
        {
            handler(ai, option, ref score);
        }
    }

    public FunctionHandlerDictionary<AISwitchBoolHandler> ShouldSwitchFunctions = [];

    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AISwitchBoolHandler> IReadOnlyExplicitAIHandlers.ShouldSwitchFunctions =>
        ShouldSwitchFunctions;

    /// <inheritdoc />
    public bool ShouldSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList<IPokemon> reserves)
    {
        var shouldSwitch = false;
        foreach (var (_, handler) in ShouldSwitchFunctions)
        {
            shouldSwitch |= handler(ai, pokemon, battle, reserves);
        }
        return shouldSwitch;
    }

    /// <summary>
    /// Functions that indicate whether we should not switch 
    /// </summary>
    public FunctionHandlerDictionary<AISwitchBoolHandler> ShouldNotSwitchFunctions = [];

    /// <inheritdoc />
    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AISwitchBoolHandler> IReadOnlyExplicitAIHandlers.ShouldNotSwitchFunctions =>
        ShouldNotSwitchFunctions;

    /// <summary>
    /// Functions to rank an ability
    /// </summary>
    public FunctionHandlerDictionary<AIScoreMoveHandler> AbilityRanking = [];

    /// <inheritdoc />
    public bool ShouldNotSwitch(IExplicitAI ai, IPokemon pokemon, IBattle battle, IReadOnlyList<IPokemon> reserves)
    {
        var shouldNotSwitch = false;
        foreach (var (_, handler) in ShouldNotSwitchFunctions)
        {
            shouldNotSwitch |= handler(ai, pokemon, battle, reserves);
        }
        return shouldNotSwitch;
    }

    /// <inheritdoc />
    IReadOnlyDictionary<StringKey, AIScoreMoveHandler> IReadOnlyExplicitAIHandlers.AbilityRanking =>
        AbilityRanking;
}