using System.Reflection.Metadata;

namespace Pilz.Plugins.Advanced;

public abstract class PluginFeature
{
    /// <summary>
    /// Fires when the plugin feature gets used.
    /// <br/>- For <see cref="PluginFunction"/> this fires on <see cref="PluginFunction.ExecuteFunction(PluginFunctionParameter?)"/>.
    /// <br/>- For <see cref="T:PluginModule"/> this fires on <see cref="T:PluginModule.CreateNewUI(PluginFunctionParameter?)"/>.
    /// <br/>- For any other custom feature implementation this may variate.
    /// </summary>
    public static event PluginFeatureExecuteEventHandler? OnExecute;

    /// <summary>
    /// The type of the feature defines where the feature get integrated.
    /// </summary>
    public string Type { get; init; }
    /// <summary>
    /// The identifier of the feature should be uniquie for the current <see cref="Type"/>.
    /// It defines a feature within a type.
    /// </summary>
    public string Identifier { get; init; }
    /// <summary>
    /// The full identifier of the feature should be uniquie and is the combination of <see cref="Type"/> and <see cref="Identifier"/>.
    /// It defines a feature across all types.
    /// </summary>
    public string FullIdentifier => GetFullIdentifier(Type, Identifier);
    /// <summary>
    /// The (display) name of the feature.
    /// </summary>
    public virtual string? Name { get; init; }
    /// <summary>
    /// The symbol for the feature.
    /// </summary>
    public virtual object? Icon { get; set; }
    /// <summary>
    /// Sets the prioritization of the feature.
    /// This will be respected on abfragen features and on inserting as items using the extension methods"/>.
    /// Some applications might implement a way to regonize feature prioritization via its own way.
    /// </summary>
    public virtual FeaturePrioritization Prioritization { get; set; }
    /// <summary>
    /// Defines if the feature is enabled/visible.
    /// </summary>
    public virtual bool Enabled { get; set; } = true;

    protected PluginFeature(string type, string identifier)
    {
        Identifier = identifier;
        Type = type;
    }

    protected PluginFeature(string type, string identifier, string? name) : this(type, identifier)
    {
        Name = name;
    }

    public static string GetFullIdentifier(string featureType, string identifier)
    {
        return $"{featureType}:{identifier}";
    }

    /// <summary>
    /// Fires the <see cref="OnExecute"/> event with the status <see cref="PluginFeatureExecuteEventType.PostEvent"/>.
    /// </summary>
    /// <param name="params">The present <see cref="PluginFunctionParameter"/>.</param>
    /// <param name="result">The resulting object that will be returned.</param>
    /// <returns>Returns true if the original function should be executed.</returns>
    protected bool OnPreExecute(PluginFunctionParameter? @params, ref object? result)
    {
        if (OnExecute != null)
        {
            var args = new PluginFeatureExecuteEventArgs(PluginFeatureExecuteEventType.PreEvent, @params);

            OnExecute.Invoke(this, args);

            if (args.Handled)
            {
                result = args.Result;
                return false;
            }
        }

        return true;
    }

    /// <summary>
    /// Fires the <see cref="OnExecute"/> event with the status <see cref="PluginFeatureExecuteEventType.PostEvent"/>.
    /// </summary>
    /// <param name="params">The present <see cref="PluginFunctionParameter"/>.</param>
    /// <param name="result">The resulting object that will be returned.</param>
    protected void OnPostExecute(PluginFunctionParameter? @params, ref object? result)
    {
        if (OnExecute != null)
        {
            var args = new PluginFeatureExecuteEventArgs(PluginFeatureExecuteEventType.PostEvent, @params)
            {
                Result = result
            };

            OnExecute.Invoke(this, args);

            if (args.Handled)
                result = args.Result;
        }
    }
}
