using e_suite.Workflow.Core.Attributes;
using e_suite.Workflow.Core.Enums;
using e_suite.Workflow.Core.Interfaces;
using e_suite.Workflow.Core.Tasks;

namespace e_suite.Workflow.Core.Extensions;

//copied from Database.core.
public class TaskDefinition
{
    public string Type { get; set; } = string.Empty;
    public int Order { get; set; }
    public Dictionary<string, object>? Config { get; set; }
}

public static class TaskExtensions
{
    public static TaskDefinition ToDefinition(this ITask task, int order)
    {
        if (task is ITemplateValidatable v)
        {
            var errors = v.ValidateForTemplate().ToList();
            if (errors.Count != 0)
                throw new InvalidOperationException(
                    $"Task {task.GetType().Name} is invalid: {string.Join("; ", errors)}");
        }

        return new TaskDefinition
        {
            Type = task.GetType().AssemblyQualifiedName!,
            Order = order,
            Config = ExtractConfig(task)
        };
    }

    private static Dictionary<string, object> ExtractConfig(object task)
    {
        var dict = new Dictionary<string, object>();

        foreach (var prop in task.GetType().GetProperties())
        {
            if (!prop.CanRead) continue;
            if (prop.GetIndexParameters().Length > 0) continue; // skip indexers
            if (prop.IsDefined(typeof(RuntimeOnlyAttribute), inherit: true))
                continue;

            var value = prop.GetValue(task);
            if (value != null)
                dict[prop.Name] = value;
        }

        return dict;
    }

    public static ITask ToTask(this TaskDefinition def)
    {
        var type = Type.GetType(def.Type, throwOnError: true)!;
        var task = (ITask)Activator.CreateInstance(type)!;

        if (def.Config != null)
            foreach (var kvp in def.Config)
            {
                var prop = type.GetProperty(kvp.Key);
                if (prop == null || !prop.CanWrite) continue;
                if (prop.IsDefined(typeof(RuntimeOnlyAttribute), inherit: true))
                    continue;

                object? converted = ConvertValue(kvp.Value, prop.PropertyType);
                prop.SetValue(task, converted);
            }

        if (task is ITemplateValidatable v)
        {
            var errors = v.ValidateForTemplate().ToList();
            if (errors.Count != 0)
                throw new InvalidOperationException(
                    $"Task {task.GetType().Name} is invalid: {string.Join("; ", errors)}");
        }

        return task;
    }

    private static object? ConvertValue(object? value, Type targetType)
    {
        if (value == null) return null;

        // Handle enums
        if (targetType.IsEnum)
            return Enum.Parse(targetType, value.ToString()!);

        // Handle Guid
        if (targetType == typeof(Guid))
            return Guid.Parse(value.ToString()!);

        // Handle nullable types
        var underlying = Nullable.GetUnderlyingType(targetType);
        if (underlying != null)
            return Convert.ChangeType(value, underlying);

        return Convert.ChangeType(value, targetType);
    }


    public static bool IsCompleted(this ITask task)
    {
        return task.TaskState.In(TaskState.Completed, TaskState.Cancelled);
    }

    public static bool ReadyToActivate(this ITask task)
    {
        foreach (var predecessor in task.Predecessors)
        {
            if (!predecessor.IsCompleted())
            {
                return false;
            }
        }

        return true;
    }
}