using Discord;

namespace EllieBot.Marmalade;

/// <summary>
/// The base class which will be loaded as a module into EllieBot
/// Any user-defined canary has to inherit from this class.
/// Canaries get instantiated ONLY ONCE during the loading,
/// and any canary commands will be executed on the same instance.
/// </summary>
public abstract class Canary : IAsyncDisposable
{
    /// <summary>
    /// Name of the canary. Defaults to the lowercase class name
    /// </summary>
    public virtual string Name
        => GetType().Name.ToLowerInvariant();

    /// <summary>
    /// The prefix required before the command name. For example
    /// if you set this to 'test' then a command called 'cmd' will have to be invoked by using
    /// '.test cmd' instead of `.cmd` 
    /// </summary>
    public virtual string Prefix
        => string.Empty;

    /// <summary>
    /// Executed once this canary has been instantiated and before any command is executed.
    /// </summary>
    /// <returns>A <see cref="ValueTask"/> representing completion</returns>
    public virtual ValueTask InitializeAsync()
        => default;

    /// <summary>
    /// Override to cleanup any resources or references which might hold this canary in memory
    /// </summary>
    /// <returns></returns>
    public virtual ValueTask DisposeAsync()
        => default;

    /// <summary>
    /// This method is called right after the message was received by the bot.
    /// You can use this method to make the bot conditionally ignore some messages and prevent further processing.
    /// <para>Execution order:</para>
    /// <para>
    /// *<see cref="ExecOnMessageAsync"/>* →
    /// <see cref="ExecInputTransformAsync"/> →
    /// <see cref="ExecPreCommandAsync"/> →
    /// <see cref="ExecPostCommandAsync"/> OR <see cref="ExecOnNoCommandAsync"/>
    /// </para>
    /// </summary>
    /// <param name="guild">Guild in which the message was sent</param>
    /// <param name="msg">Message received by the bot</param>
    /// <returns>A <see cref="ValueTask"/> representing whether the message should be ignored and not processed further</returns>
    public virtual ValueTask<bool> ExecOnMessageAsync(IGuild? guild, IUserMessage msg)
        => default;

    /// <summary>
    /// Override this method to modify input before the bot searches for any commands matching the input
    /// Executed after <see cref="ExecOnMessageAsync"/>
    /// This is useful if you want to reinterpret the message under some conditions
    /// <para>Execution order:</para>
    /// <para>
    /// <see cref="ExecOnMessageAsync"/> →
    /// *<see cref="ExecInputTransformAsync"/>* →
    /// <see cref="ExecPreCommandAsync"/> →
    /// <see cref="ExecPostCommandAsync"/> OR <see cref="ExecOnNoCommandAsync"/>
    /// </para> 
    /// </summary>
    /// <param name="guild">Guild in which the message was sent</param>
    /// <param name="channel">Channel in which the message was sent</param>
    /// <param name="user">User who sent the message</param>
    /// <param name="input">Content of the message</param>
    /// <returns>A <see cref="ValueTask"/> representing new, potentially modified content</returns>
    public virtual ValueTask<string?> ExecInputTransformAsync(
        IGuild? guild,
        IMessageChannel channel,
        IUser user,
        string input
    )
        => default;

    /// <summary>
    /// This method is called after the command was found but not executed,
    /// and can be used to prevent the command's execution.
    /// The command information doesn't have to be from this canary as this method
    /// will be called when *any* command from any module or canary was found.
    /// You can choose to prevent the execution of the command by returning "true" value.
    /// <para>Execution order:</para>
    /// <para>
    /// <see cref="ExecOnMessageAsync"/> →
    /// <see cref="ExecInputTransformAsync"/> →
    /// *<see cref="ExecPreCommandAsync"/>* →
    /// <see cref="ExecPostCommandAsync"/> OR <see cref="ExecOnNoCommandAsync"/>
    /// </para>
    /// </summary>
    /// <param name="context">Command context</param>
    /// <param name="moduleName">Name of the canary or module from which the command originates</param>
    /// <param name="commandName">Name of the command which is about to be executed</param>
    /// <returns>A <see cref="ValueTask"/> representing whether the execution should be blocked</returns>
    public virtual ValueTask<bool> ExecPreCommandAsync(
        AnyContext context,
        string moduleName,
        string commandName
    )
        => default;

    /// <summary>
    /// This method is called after the command was succesfully executed.
    /// If this method was called, then <see cref="ExecOnNoCommandAsync"/> will not be executed
    /// <para>Execution order:</para>
    /// <para>
    /// <see cref="ExecOnMessageAsync"/> →
    /// <see cref="ExecInputTransformAsync"/> →
    /// <see cref="ExecPreCommandAsync"/> →
    /// *<see cref="ExecPostCommandAsync"/>* OR <see cref="ExecOnNoCommandAsync"/>
    /// </para>
    /// </summary>
    /// <returns>A <see cref="ValueTask"/> representing completion</returns>
    public virtual ValueTask ExecPostCommandAsync(AnyContext ctx, string moduleName, string commandName)
        => default;

    /// <summary>
    /// This method is called if no command was found for the input.
    /// Useful if you want to have games or features which take arbitrary input
    /// but ignore any messages which were blocked or caused a command execution
    /// If this method was called, then <see cref="ExecPostCommandAsync"/> will not be executed
    /// <para>Execution order:</para>
    /// <para>
    /// <see cref="ExecOnMessageAsync"/> →
    /// <see cref="ExecInputTransformAsync"/> →
    /// <see cref="ExecPreCommandAsync"/> →
    /// <see cref="ExecPostCommandAsync"/> OR *<see cref="ExecOnNoCommandAsync"/>*
    /// </para>
    /// </summary>
    /// <returns>A <see cref="ValueTask"/> representing completion</returns>
    public virtual ValueTask ExecOnNoCommandAsync(IGuild? guild, IUserMessage msg)
        => default;
}

public readonly struct ExecResponse
{
}