using e_suite.Database.Core.Models;
using e_suite.Database.Core.Tables.Contacts;
using e_suite.Database.Core.Tables.Domain;
using e_suite.Workflow.Core.Attributes;
using e_suite.Workflow.Core.Interfaces;
using eSuite.Core.Miscellaneous;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace e_suite.Workflow.Core.Extensions;

public static class TaskExtensions
{
    private static Dictionary<string, object?> ToConfigDictionary(this ITask task)
    {
        var dictionary = new Dictionary<string, object?>();

        var type = task.GetType();
        var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (var prop in properties)
        {
            if (!prop.CanRead)
                continue;

            if (!prop.CanWrite)
                continue;

            var value = prop.GetValue(task);

            dictionary[prop.Name] = value;
        }

        return dictionary;
    }

    public static ITask ToTask(this TaskDefinition definition, JsonSerializerOptions jsonSerializerOptions)
    {
        var type = Type.GetType(definition.Type);

        if (type == null)
            throw new InvalidOperationException($"Unknown task type '{definition.Type}'.");

        var instance = Activator.CreateInstance(type) as ITask;

        if (instance == null)
            throw new InvalidOperationException($"Type '{definition.Type}' does not implement ITask.");

        FromConfigDictionary(instance, definition.Config, jsonSerializerOptions);

        return instance;
    }

    public static void FromConfigDictionary(this object obj, Dictionary<string, object?> dict, JsonSerializerOptions jsonSerializerOptions)
    {
        var type = obj.GetType();

        foreach (var kvp in dict)
        {
            var prop = type.GetProperty(
                kvp.Key,
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase
            );

            if (prop == null || !prop.CanWrite)
                continue;

            var targetType = prop.PropertyType;
            var value = kvp.Value;

            // If the value is a JsonElement, convert it to the target type
            if (value is JsonElement je)
            {
                if (targetType.IsEnum)
                {
                    // Enums come through as strings
                    value = Enum.Parse(targetType, je.GetString()!, ignoreCase: true);
                }
                else
                {
                    // Deserialize JSON into the target type (handles lists, objects, primitives)
                    value = JsonSerializer.Deserialize(je.GetRawText(), targetType, jsonSerializerOptions);
                }
            }

            prop.SetValue(obj, value);
        }
    }

    public static TaskDefinition ToDefinition(this ITask task)
    {
        if (task is null)
            throw new NullReferenceException();


        return new TaskDefinition
        {
            Type = task.GetType().FullName,
            Config = task.ToConfigDictionary()
        };
    }

    //public static TaskDefinition ToDefinition(this ITask task)
    //{
    //    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().FullName!,
    //        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);
    //}


    //Todo can only be on a run-time instance
    //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;
    //}
}