using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Shapes;
using Size = System.Windows.Size;

namespace Nodify.Calculator
{
    public static class OperationFactory
    {

        public static List<OperationInfoViewModel> GetSystemNodes()
        {
            List<OperationInfoViewModel> systemNodes = new List<OperationInfoViewModel>();

            var copynode = new OperationInfoViewModel()
            {
                Title = "COPY",
                Type = OperationType.System,
                OPType = "copy",
                IsFlowNode = false,
            };
            copynode.Input.Add("");
            copynode.Output.Add("");
            copynode.Output.Add("");

            var begin = new OperationInfoViewModel()
            {
                Title = "Begin",
                Type = OperationType.System,
                sysOp = SystemOperations.BEGIN,
                IsFlowNode = true
            };
            begin.Output.Add("");

            var ending = new OperationInfoViewModel()
            {
                Title = "End",
                Type = OperationType.System,
                sysOp = SystemOperations.END,
                IsFlowNode = true
            };
            ending.Input.Add("");

            var debugAndCreateModels = new OperationInfoViewModel()
            {
                Title = "Debug & Create Model",
                Type = OperationType.System,
                sysOp = SystemOperations.DEBUG_AND_CREATE_MODEL,
                IsFlowNode = true
            };
            debugAndCreateModels.Input.Add("");
            debugAndCreateModels.Output.Add("");

            var jsonParseNode = new OperationInfoViewModel()
            {
                Title = "Parse Json",
                Type = OperationType.System,
                sysOp = SystemOperations.PARSEJSON,
                IsFlowNode = false
            };
            jsonParseNode.Input.Add("");
            jsonParseNode.Output.Add("");

            var splitNode = new OperationInfoViewModel()
            {
                Title = "Split",
                Type = OperationType.System,
                sysOp = SystemOperations.SPLIT,
                IsFlowNode = false
            };
            splitNode.Input.Add("");
            splitNode.Output.Add("");

            var takeNode = new OperationInfoViewModel()
            {
                Title = "TAKE",
                Type = OperationType.System,
                sysOp = SystemOperations.TAKE,
                IsFlowNode = false
            };
            takeNode.Input.Add("");
            takeNode.Output.Add("");

            var authNode = new OperationInfoViewModel()
            {
                Title = "Auth",
                Type = OperationType.System,
                sysOp = SystemOperations.AUTH,
                IsFlowNode = true
            };
            authNode.Input.Add("Base URL");
            authNode.Input.Add("Auth Type");
            authNode.Input.Add("Token");
            authNode.Input.Add("API Key");
            authNode.Input.Add("Username");
            authNode.Input.Add("Password");

            systemNodes.Add(authNode);
            systemNodes.Add(copynode);
            systemNodes.Add(begin);
            systemNodes.Add(ending);
            systemNodes.Add(debugAndCreateModels);
            systemNodes.Add(jsonParseNode);
            systemNodes.Add(splitNode);
            systemNodes.Add(takeNode);
            return systemNodes;
        }

        public static List<OperationInfoViewModel> GetOperationsInfo(Type container)
        {
            List<OperationInfoViewModel> result = new List<OperationInfoViewModel>();

            foreach (var method in container.GetMethods())
            {
                if (method.IsStatic)
                {
                    OperationInfoViewModel op = new OperationInfoViewModel
                    {
                        Title = method.Name
                    };

                    var attr = method.GetCustomAttribute<OperationAttribute>();
                    var para = method.GetParameters();

                    bool generateInputNames = true;

                    op.Type = OperationType.Normal;

                    if (para.Length == 2)
                    {
                        var delType = typeof(Func<double, double, double>);
                        var del = (Func<double, double, double>)Delegate.CreateDelegate(delType, method);

                        op.Operation = new BinaryOperation(del);
                    }
                    else if (para.Length == 1)
                    {
                        if (para[0].ParameterType.IsArray)
                        {
                            op.Type = OperationType.Expando;

                            var delType = typeof(Func<double[], double>);
                            var del = (Func<double[], double>)Delegate.CreateDelegate(delType, method);

                            op.Operation = new ParamsOperation(del);
                            op.MaxInput = int.MaxValue;
                        }
                        else
                        {
                            var delType = typeof(Func<double, double>);
                            var del = (Func<double, double>)Delegate.CreateDelegate(delType, method);

                            op.Operation = new UnaryOperation(del);
                        }
                    }
                    else if (para.Length == 0)
                    {
                        var delType = typeof(Func<double>);
                        var del = (Func<double>)Delegate.CreateDelegate(delType, method);

                        op.Operation = new ValueOperation(del);
                    }

                    if (attr != null)
                    {
                        op.MinInput = attr.MinInput;
                        op.MaxInput = attr.MaxInput;
                        generateInputNames = attr.GenerateInputNames;
                    }
                    else
                    {
                        op.MinInput = (uint)para.Length;
                        op.MaxInput = (uint)para.Length;
                    }

                    foreach (var param in para)
                    {
                        op.Input.Add(generateInputNames ? param.Name : null);
                    }

                    for (int i = op.Input.Count; i < op.MinInput; i++)
                    {
                        op.Input.Add(null);
                    }
                    op.Output.Add("");
                    result.Add(op);
                }
            }

            return result;
        }

        public static OperationViewModel GetOperation(OperationInfoViewModel info)
        {
            var input = info.Input.Select(i => new ConnectorViewModel
            {
                Title = i
            });

            switch (info.Type)
            {
                case OperationType.Expression:
                    var eo = new ExpressionOperationViewModel
                    {
                        Title = info.Title,
                        Operation = info.Operation,
                        Expression = "1 + sin {a} + cos {b}"
                    };
                    eo.Output.Add(new ConnectorViewModel());
                    return eo;
                case OperationType.Calculator:
                    return new CalculatorOperationViewModel
                    {
                        Title = info.Title,
                        Operation = info.Operation,
                    };

                case OperationType.Expando:
                    var o = new ExpandoOperationViewModel
                    {
                        MaxInput = info.MaxInput,
                        MinInput = info.MinInput,
                        Title = info.Title,
                        Operation = info.Operation
                    };
                    o.Output.Add(new ConnectorViewModel());
                    o.Input.AddRange(input);
                    return o;

                case OperationType.Group:
                    return new OperationGroupViewModel
                    {
                        Title = info.Title,
                    };

                case OperationType.Graph:
                    return new OperationGraphViewModel
                    {
                        Title = info.Title,
                        DesiredSize = new Size(420, 250)
                    };

                case OperationType.API:
                    var _o = new APIOperationViewModel
                    {
                        Title = info.Title,
                        OperationType = info.OPType.ToUpper()
                    };
                    var connectorViewModel = new ConnectorViewModel()
                    {
                        Title = "",
                        Shape = ConnectorShape.Triangle
                    };
                    var connectorViewModel2 = new ConnectorViewModel()
                    {
                        Title = "",
                        Shape = ConnectorShape.Triangle,
                        IsInput = false
                    };
                    _o.Output.Add(connectorViewModel2);
                    _o.Output.Add(new ConnectorViewModel());
                    _o.Input.Add(connectorViewModel);
                    foreach (var item in input)
                    {
                        item.ConnectorColor = Color.GreenYellow;
                        _o.Input.Add(item);
                    }
                    //_o.Input.AddRange(input);
                    return _o;
                case OperationType.System:
                    if (info.sysOp == SystemOperations.FUNCTION)
                    {
                        var funcOp = new FunctionOperationViewModel
                        {
                            Title = info.Title,
                            FunctionName = info.Title
                        };
                        // Add flow connectors (triangle)
                        var funcFlowIn = new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle };
                        var funcFlowOut = new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false };
                        funcOp.Input.Add(funcFlowIn);
                        funcOp.Output.Add(funcFlowOut);

                        // Configure typed input/output parameters
                        funcOp.ConfigureParameters(info.FunctionInputs, info.FunctionOutputs);
                        return funcOp;
                    }

                    if (info.sysOp == SystemOperations.AUTH)
                    {
                        var authOp = new AuthOperationViewModel
                        {
                            Title = info.Title,
                            SystemOperationType = SystemOperations.AUTH
                        };
                        // Add flow connectors (triangle)
                        var flowIn = new ConnectorViewModel()
                        {
                            Title = "",
                            Shape = ConnectorShape.Triangle
                        };
                        var flowOut = new ConnectorViewModel()
                        {
                            Title = "",
                            Shape = ConnectorShape.Triangle,
                            IsInput = false
                        };
                        authOp.Input.Add(flowIn);
                        authOp.Output.Add(flowOut);
                        // Add data input connectors
                        foreach (var inp in input)
                        {
                            inp.ConnectorColor = System.Drawing.Color.Orange;
                            authOp.Input.Add(inp);
                        }
                        return authOp;
                    }

                    var sysOp = new SystemOperationViewModel
                    {
                        Title = info.Title,
                        SystemOperationType = info.sysOp
                    };

                    if (info.sysOp == SystemOperations.GET_SET && info.IsModelNode)
                    {
                        if (info.Title == "GET")
                        {
                            info.Output.Add("");
                            info.IsFlowNode = false;
                        }
                        else if (info.Title == "SET")
                        {
                            info.Input.Add("");
                            var customModelDir = "CustomModels";
                            Directory.CreateDirectory(customModelDir);
                            var flpath = System.IO.Path.Combine(customModelDir, info.ClassName + ".cs");
                            if (File.Exists(flpath))
                            {
                                //Read all the properties from the file
                                var fileContent = File.ReadAllText(flpath);

                                // Parse and analyze properties
                                var properties = GetPropertiesFromClass(fileContent);

                                // Print out the extracted properties and their types
                                Console.WriteLine("Properties found:");
                                foreach (var property in properties)
                                {
                                    Console.WriteLine($"Property Name: {property.Name}, Type: {property.Type}");
                                    info.Output.Add(property.Name);
                                }
                            }


                            info.IsFlowNode = true;
                        }
                        var flTitle = $"{info.Title} {info.ClassName}";
                        sysOp.Title = flTitle;
                    }

                    if (info.sysOp == SystemOperations.GET_SET && info.IsSimpleVariable)
                    {
                        var varLabel = $"{info.Title} {info.ClassName} ({info.VariableType})";
                        sysOp.Title = varLabel;

                        if (info.Title == "GET")
                        {
                            // GET variable: output the value, no flow needed
                            info.Output.Add("Value");
                            info.IsFlowNode = false;
                        }
                        else if (info.Title == "SET")
                        {
                            // SET variable: input connector for the value, flow node
                            info.Input.Add("Value");
                            info.IsFlowNode = true;
                        }
                    }

                    if (info.sysOp != SystemOperations.BEGIN &&
                        info.sysOp != SystemOperations.END &&
                        info.IsFlowNode)
                    {
                        var flowinputnode = new ConnectorViewModel()
                        {
                            Title = "",
                            Shape = ConnectorShape.Triangle
                        };
                        var flowoutputnode = new ConnectorViewModel()
                        {
                            Title = "",
                            Shape = ConnectorShape.Triangle,
                            IsInput = false
                        };
                        sysOp.Input.Add(flowinputnode);
                        sysOp.Output.Add(flowoutputnode);
                    }
                    foreach (var item in info.Output)
                    {
                        var out1 = new ConnectorViewModel()
                        {
                            Title = string.IsNullOrEmpty(item) ? "" : item,
                            IsInput = false,
                            ConnectorColor = Color.DarkRed,
                            Shape = (info.sysOp == SystemOperations.BEGIN || info.sysOp == SystemOperations.END) ? ConnectorShape.Triangle : ConnectorShape.Circle,
                        };
                        if (out1.Shape != ConnectorShape.Triangle)
                        {
                            out1.ConnectorColor = Color.DeepPink;
                        }
                        sysOp.Output.Add(out1);
                    }
                    foreach (var item in input)
                    {
                        item.Shape = (info.sysOp == SystemOperations.BEGIN || info.sysOp == SystemOperations.END) ? ConnectorShape.Triangle : ConnectorShape.Circle;
                        sysOp.Input.Add(item);
                    }
                    return sysOp;
                default:
                    {
                        var op = new OperationViewModel
                        {
                            Title = info.Title,
                            Operation = info.Operation,
                        };
                        var ccv = new ConnectorViewModel()
                        {
                            IsInput = false,
                            Shape = ConnectorShape.Circle,
                            Title = ""
                        };
                        op.Output.Add(ccv);

                        op.Input.AddRange(input);
                        return op;
                    }
            }
        }

        static List<CustomProperty> GetPropertiesFromClass(string classContent)
        {
            // Parse the C# class content using Roslyn
            var syntaxTree = CSharpSyntaxTree.ParseText(classContent);
            var root = syntaxTree.GetRoot();

            // Find the first class declaration (you can refine this if there are multiple classes)
            var classDeclaration = root.DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault();

            // List to store extracted properties
            var properties = new List<CustomProperty>();

            if (classDeclaration != null)
            {
                // Look for property declarations inside the class
                var propertyDeclarations = classDeclaration.Members.OfType<PropertyDeclarationSyntax>();
                foreach (var property in propertyDeclarations)
                {
                    var propertyName = property.Identifier.Text; // Property name
                    var propertyType = property.Type.ToString(); // Property type

                    // Add the property and its type to the list
                    properties.Add(new CustomProperty
                    {
                        Name = propertyName,
                        Type = propertyType
                    });
                }
            }

            return properties;
        }
    }
}

class CustomProperty // <- Renamed to avoid conflicts with System.Reflection.PropertyInfo
{
    public string Name { get; set; }
    public string Type { get; set; }
}
