using DevExpress.Blazor;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Web;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using TIAM.Entities.TransferDestinations;
using TIAMWebApp.Shared.Application.Utility;
using TIAMWebApp.Shared.Application.Models.ClientSide.UI.WizardModels;
using TIAMWebApp.Shared.Application.Models.PageModels;
using DevExpress.Pdf.Native.BouncyCastle.Asn1.X509.Qualified;
using Microsoft.Extensions.Localization;
using TIAM.Resources;
using TIAMWebApp.Shared.Application.Interfaces;
using System.Reflection;
using TIAMWebApp.Shared.Application.Models;
using DevExpress.XtraPrinting;
using TIAMSharedUI.Shared;
using DevExpress.Pdf.Native.BouncyCastle.Asn1.Cms;
using System.Text.RegularExpressions;


namespace TIAMSharedUI.Pages.Components
{
    public partial class InputWizard : ComponentBase
    {
        [Inject]
        public required LogToBrowserConsole LogToBrowserConsole { get; set; }

        [Inject]
        IStringLocalizer<TIAMResources> localizer { get; set; }

        public Dictionary<int, Guid> FormSteps { get; set; } = new Dictionary<int, Guid>();
        public int CurrentStep { get; set; } = 0;

        //TestUserData Data { get; set; } = new TestUserData();

        [Parameter]
        public string TitleResourceString { get; set; } = "Wizard";

        [Parameter]
        public string SubtitleResourceString { get; set; } = "Let's fill in this form";

        [Parameter]
        public object Data { get; set; } = new object();

        [Parameter]
        public EventCallback<object> OnSubmit { get; set; }

        [Parameter]
        public string SubmitButtonText { get; set; } = "Submit";

        [Required]
        [Parameter]
        public List<string> IgnoreReflection { get; set; }



        string _formSubmitResult = "";
        private string _spinnerClass = "";

        async Task HandleValidSubmit()
        {
            _spinnerClass = "spinner-border spinner-border-sm";
            await Task.Delay(500);

            string debugString = "Success: ";

            Type myType = Data.GetType();
            IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());

            foreach (PropertyInfo prop in props)
            {
                object propValue = prop.GetValue(Data, null);

                // Do something with propValue
                debugString += $"{prop.Name} = {propValue}\n";
            }

            _formSubmitResult = debugString;
            _spinnerClass = "";

            await OnSubmit.InvokeAsync(Data);

        }
        void HandleInvalidSubmit()
        {
            _formSubmitResult = "Please correct all errors";
        }

        public void OnNext(MouseEventArgs args)
        {
            LogToBrowserConsole.LogToBC("OnNext called");
            CurrentStep++;
        }

        public void OnPrevious(MouseEventArgs args)
        {
            LogToBrowserConsole.LogToBC("OnPrev called");
            CurrentStep--;
        }

        public RenderFragment CreateEditFormFields() => formLayoutBuilder =>
        {

            var _type = Data.GetType();
            LogToBrowserConsole.LogToBC("Hellooooo " + _type.AssemblyQualifiedName);

            var propertyList = _type.GetProperties();
            var _length = propertyList.Length - IgnoreReflection.Count;
            //var propertyList = typeof(TestUserData).GetProperties();
            formLayoutBuilder.OpenComponent<DxFormLayout>(0);

            formLayoutBuilder.AddAttribute(1, "ChildContent", (RenderFragment)((layoutItemBuilder) =>
            {
                int i = 0;
                int k = 0;
                foreach (var property in propertyList)
                {
                    bool isActive = k == CurrentStep;
                    //if (property.Name == "Id" || property.Name == "Latitude" || property.Name == "Longitude" || property.Name == "Created" || property.Name == "Modified")
                    //if (property.Name == "Id" || property.Name == "Created" || property.Name == "Modified")
                    if (IgnoreReflection.Contains(property.Name))
                    {
                        continue;
                    }

                    Guid _stepID = Guid.Empty;

                    if (!FormSteps.ContainsKey(k))
                    {
                        _stepID = Guid.NewGuid();
                        LogToBrowserConsole.LogToBC($"Adding step {k}, {_stepID}, for {property.Name}");
                        FormSteps.Add(k, _stepID);
                    }
                    else
                    {
                        _stepID = FormSteps[k];
                    }

                    //the following line creates an expression that accesses the property value by name
                    var access = Expression.Property(Expression.Constant(Data), property.Name);
                    //the following line creates a lambda expression that returns the value of the property
                    var lambda = Expression.Lambda(typeof(Func<>).MakeGenericType(property.PropertyType), access);

                    LogToBrowserConsole.LogToBC(lambda.ToString());

                    layoutItemBuilder.OpenElement(i++, "div");//open div
                    layoutItemBuilder.AddAttribute(i++, "id", _stepID.ToString());
                    layoutItemBuilder.AddAttribute(i++, "class", "disply-flex align-items-center ");
                    layoutItemBuilder.AddAttribute(i++, "style", "width: 100%;");
                    if (k != CurrentStep)
                    {
                        layoutItemBuilder.AddAttribute(i++, "hidden", "true");
                    }
                    else
                    {
                        //this input should be focused, so we set a flag
                        LogToBrowserConsole.LogToBC($"Setting focus to {property.Name}");

                    }

                    DataTypeAttribute attrList = (DataTypeAttribute)property.GetCustomAttributes(typeof(DataTypeAttribute), false).First();
                    DisplayAttribute displayLabel = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayAttribute), false).First();
                    layoutItemBuilder.OpenComponent<DxFormLayoutItem>(i++); //open dxformlayoutitem
                    layoutItemBuilder.AddAttribute(i++, "Caption", localizer.GetString(displayLabel.Name));
                    layoutItemBuilder.AddAttribute(i++, "ColSpanMd", 12);
                    layoutItemBuilder.AddAttribute(i++, "CaptionPosition", CaptionPosition.Vertical);
                    layoutItemBuilder.AddAttribute(i++, "CssClass", "justify-content-center");

                    layoutItemBuilder.AddAttribute(i++, "Template", (RenderFragment<object>)((context) => ((editor) =>
                    {
                        var j = 0;
                        switch (attrList.DataType)
                        {

                            case DataType.Text:
                                {
                                    editor.OpenComponent<DxTextBox>(j++);
                                    LogToBrowserConsole.LogToBC($"{property.Name}, {property.PropertyType}");
                                    editor.AddAttribute(j++, "Text", property.GetValue(Data));
                                    editor.AddAttribute(j++, "TextExpression", lambda);
                                    editor.AddAttribute(j++, "CssClass", "form-field");

                                    if (isActive)
                                    {
                                        editor.AddAttribute(j++, "Id", "ActiveInput");
                                    }
                                    editor.AddAttribute(j++, "TextChanged", EventCallback.Factory.Create<string>(this, str => { property.SetValue(Data, str); }));
                                    editor.CloseComponent();

                                    break;
                                }
                            case DataType.Password:
                                {

                                    editor.OpenComponent<DxTextBox>(j++);
                                    editor.AddAttribute(j++, "Password", true);
                                    editor.AddAttribute(j++, "NullText", "Password");
                                    editor.AddAttribute(j++, "Text", property.GetValue(Data));
                                    editor.AddAttribute(j++, "TextExpression", lambda);
                                    editor.AddAttribute(j++, "TextChanged", EventCallback.Factory.Create<string>(this, str => { property.SetValue(Data, str); }));
                                    editor.CloseComponent();
                                    break;
                                }
                            case DataType.PhoneNumber:
                                {
                                    editor.OpenComponent<DxMaskedInput<string>>(j++);
                                    editor.AddAttribute(j++, "Value", property.GetValue(Data));

                                    editor.AddAttribute(j++, "Mask", TIAMRegularExpressions.PhoneNumberMask);
                                    editor.AddAttribute(j++, "MaskMode", MaskMode.RegEx);
                                    editor.AddAttribute(j++, "BindValueMode", BindValueMode.OnInput);
                                    editor.AddAttribute(j++, "NullText", "+11234567890");
                                    editor.AddAttribute(j++, "MaskAutoCompleteMode", MaskAutoCompleteMode.None);
                                    editor.AddAttribute(j++, "ValueExpression", lambda);
                                    //editor.AddAttribute(j++, "Placeholder", "#");
                                    //editor.AddAttribute(j++, "PlaceholdersVisible", false);
                                    editor.AddAttribute(j++, "ValueChanged", EventCallback.Factory.Create<string>(this, str => SetPhoneNumber(property, Data, str)));
                                    //editor.AddAttribute(j++, "TextChanged", EventCallback.Factory.Create<string>(this, str => { property.SetValue(Data, str); }));
                                    /*editor.AddAttribute(i++, "Template", (RenderFragment<object>)((context) => ((regexProperties) =>
                                    {                                       
                                        var l = 0;
                                        regexProperties.OpenComponent<DxRegExMaskProperties>(l++);
                                        regexProperties.AddAttribute(l++, "Placeholder", "#");
                                        regexProperties.AddAttribute(l++, "PlaceholdersVisible", false);
                                        regexProperties.CloseComponent();
                                    })));*/
                                    editor.CloseComponent();
                                    break;
                                }
                            case DataType.EmailAddress:
                                {
                                    editor.OpenComponent<DxMaskedInput<string>>(j++);
                                    editor.AddAttribute(j++, "Value", property.GetValue(Data));
                                    editor.AddAttribute(j++, "Mask", TIAMRegularExpressions.EmailMask);
                                    editor.AddAttribute(j++, "MaskMode", MaskMode.RegEx);

                                    editor.AddAttribute(j++, "BindValueMode", BindValueMode.OnInput);
                                    //MaskAutoCompleteMode="@((MaskAutoCompleteMode)AutoCompleteMode)"
                                    editor.AddAttribute(j++, "MaskAutoCompleteMode", MaskAutoCompleteMode.None);
                                    editor.AddAttribute(j++, "NullText", "example@example.com");
                                    editor.AddAttribute(j++, "ValueExpression", lambda);

                                    editor.AddAttribute(j++, "Placeholder", "#");
                                    editor.AddAttribute(j++, "PlaceholdersVisible", false);

                                    editor.AddAttribute(j++, "ValueChanged", EventCallback.Factory.Create<string>(this, str => { property.SetValue(Data, str); }));
                                    //editor.AddAttribute(j++, "Template", (RenderFragment<object>)((context) => ((editor2) =>
                                    //{
                                    //    var l = 0;
                                    //    editor2.OpenComponent<DxRegExMaskProperties>(l++);
                                    //    editor2.AddAttribute(l++, "Placeholder", "#");
                                    //    editor2.AddAttribute(l++, "PlaceholdersVisible", "false");
                                    //    editor2.CloseComponent();
                                    //})));
                                    editor.CloseComponent();
                                    break;
                                }
                            case DataType.Date:
                                {
                                    editor.OpenComponent<DxDateEdit<DateTime>>(j);
                                    editor.AddAttribute(j++, "Date", property.GetValue(Data));
                                    editor.AddAttribute(j++, "DateExpression", lambda);
                                    editor.AddAttribute(j++, "CssClass", "form-field");
                                    editor.AddAttribute(j++, "DateChanged", EventCallback.Factory.Create<DateTime>(this, str => { property.SetValue(Data, str); }));
                                    editor.CloseComponent();
                                    break;

                                }
                            case DataType.Custom:
                                {
                                    if (property.PropertyType == typeof(double))
                                    {

                                        editor.OpenComponent<DxMaskedInput<double>>(j);

                                        editor.AddAttribute(j++, "Value", property.GetValue(Data));

                                        editor.AddAttribute(j++, "Mask", "n6");
                                        editor.AddAttribute(j++, "BindValueMode", BindValueMode.OnInput);
                                        editor.AddAttribute(j++, "ValueExpression", lambda);
                                        editor.AddAttribute(j++, "CssClass", "form-field");
                                        editor.AddAttribute(j++, "ValueChanged", EventCallback.Factory.Create<double>(this, str => { property.SetValue(Data, str); }));
                                        editor.CloseComponent();
                                        break;
                                    }
                                    else if (property.PropertyType == typeof(int))
                                    {

                                        editor.OpenComponent<DxMaskedInput<int>>(j);

                                        editor.AddAttribute(j++, "Value", property.GetValue(Data));
                                        editor.AddAttribute(j++, "BindValueMode", BindValueMode.OnInput);
                                        editor.AddAttribute(j++, "Mask", NumericMask.WholeNumber);
                                        editor.AddAttribute(j++, "ValueExpression", lambda);
                                        editor.AddAttribute(j++, "CssClass", "form-field");
                                        editor.AddAttribute(j++, "ValueChanged", EventCallback.Factory.Create<int>(this, str => { property.SetValue(Data, str); }));
                                        editor.CloseComponent();
                                        break;
                                    }
                                    else if (property.PropertyType == typeof(IEnumerable<string>) && property.Name == "Occupation")
                                    {

                                        editor.OpenComponent<DxComboBox<string, string>>(j);
                                        editor.AddAttribute(j++, "Data", AdditionalData.Occupations);
                                        editor.AddAttribute(j++, "Value", property.GetValue(Data));
                                        editor.AddAttribute(j++, "ValueExpression", lambda);
                                        editor.AddAttribute(j++, "ValueChanged", EventCallback.Factory.Create<string>(this, str => { property.SetValue(Data, str); }));
                                        editor.CloseComponent();
                                        break;
                                    }
                                    else if (property.PropertyType == typeof(DriverModel) && property.Name == "Driver")
                                    {
                                        //load possible drivers for this serviceprovider

                                        //DriverModel driver = (DriverModel)property.GetValue(Data);
                                        IEnumerable<DriverModel> drivers = new DriverModel[] {
                                            new DriverModel(Guid.NewGuid(), Guid.NewGuid(), "John Doe"),
                                            new DriverModel(Guid.NewGuid(), Guid.NewGuid(), "Jane Doe"),
                                            new DriverModel(Guid.NewGuid(), Guid.NewGuid(), "James Doe")
                                        };

                                        var defaultDriver = drivers.FirstOrDefault();

                                        //editor.OpenElement(j++, "p");
                                        //editor.AddContent(j++, localizer.GetString("Driver"));
                                        //editor.CloseElement();
                                        editor.OpenComponent<DxComboBox<DriverModel, DriverModel>>(j);
                                        editor.AddAttribute(j++, "Data", drivers);
                                        editor.AddAttribute(j++, "TextFieldName", nameof(defaultDriver.Name));
                                        editor.AddAttribute(j++, "Value", drivers.FirstOrDefault());
                                        //editor.AddAttribute(j++, "Text", defaultDriver);
                                        editor.AddAttribute(j++, "CssClass", "form-field");
                                        //editor.AddAttribute(j++, "TextExpression", lambda);
                                        editor.AddAttribute(j++, "ValueExpression", lambda);
                                        editor.AddAttribute(j++, "ValueChanged", EventCallback.Factory.Create<DriverModel>(this, str => { property.SetValue(Data, str); }));
                                        editor.CloseComponent();
                                        break;
                                    }
                                    //string.Compare(metadata.CustomDataType, "BoldRed", true) == 0
                                    //else if (property.PropertyType == typeof(string) && property.Name == "Destination")

                                    else if (property.PropertyType == typeof(string) && string.Compare(attrList.CustomDataType, "TransferDestination", true) == 0)
                                    {

                                        editor.OpenComponent<SliderItemSelector>(j);
                                        editor.AddAttribute(j++, "OwlId", "owlSelector" + _stepID);
                                        editor.AddAttribute(j++, "TextValue", property.GetValue(Data));
                                        //editor.AddAttribute(j++, "TExpression", lambda);
                                        editor.AddAttribute(j++, "OnSliderChanged", EventCallback.Factory.Create<string>(this, result =>
                                        {
                                            LogToBrowserConsole.LogToBC($"Slider changed to {result}");
                                            property.SetValue(Data, result);
                                            LogToBrowserConsole.LogToBC($"bleh: {property.Name} = {property.GetValue(Data)}");
                                            StateHasChanged(); // Add this line to refresh the UI
                                        }));

                                        editor.CloseComponent();
                                        editor.OpenComponent<DxTextBox>(j++);
                                        /*editor.AddAttribute(j++, "CssClass", "form-field");*/
                                        editor.AddAttribute(j++, "NullText", "Slide or type");
                                        editor.AddAttribute(j++, "Enabled", false);
                                        editor.AddAttribute(j++, "Text", property.GetValue(Data));
                                        editor.AddAttribute(j++, "TextExpression", lambda);
                                        editor.AddAttribute(j++, "TextChanged", EventCallback.Factory.Create<string>(this, str =>
                                        {
                                            property.SetValue(Data, str);
                                            LogToBrowserConsole.LogToBC($"bleh: {property.Name} = {property.GetValue(Data)}");
                                        }));
                                        editor.CloseComponent();

                                    }

                                    break;

                                }
                            case DataType.MultilineText:
                                {
                                    editor.OpenComponent<DxMemo>(j);
                                    editor.AddAttribute(j++, "Text", property.GetValue(Data));
                                    editor.AddAttribute(j++, "TextExpression", lambda);
                                    editor.AddAttribute(j++, "CssClass", "form-field");
                                    editor.AddAttribute(j++, "TextChanged", EventCallback.Factory.Create<string>(this, str => { property.SetValue(Data, str); }));
                                    editor.CloseComponent();
                                    break;

                                }
                            default:
                                break;
                        }
                    })));

                    layoutItemBuilder.CloseComponent(); //close dxformlayoutitem

                    layoutItemBuilder.OpenComponent<DxButton>(i++);
                    layoutItemBuilder.AddAttribute(i++, "Click", EventCallback.Factory.Create<MouseEventArgs>(this, OnPrevious));
                    layoutItemBuilder.AddAttribute(i++, "SubmitFormOnClick", false);
                    layoutItemBuilder.AddAttribute(i++, "CssClass", "btn btn-secondary mt-3");
                    if (!(k > 0))
                    {
                        layoutItemBuilder.AddAttribute(i++, "disabled", "true");
                        
                    }
                    layoutItemBuilder.AddAttribute(i++, "style", "margin-left: auto; width:49%");
                    layoutItemBuilder.AddAttribute(i++, "Text", localizer.GetString(ResourceKeys.ButtonPrevious));
                    layoutItemBuilder.CloseComponent();


                    layoutItemBuilder.OpenComponent<DxButton>(i++);
                    layoutItemBuilder.AddAttribute(i++, "Click", EventCallback.Factory.Create<MouseEventArgs>(this, OnNext));
                    layoutItemBuilder.AddAttribute(i++, "SubmitFormOnClick", false);
                    layoutItemBuilder.AddAttribute(i++, "CssClass", "btn btn-primary mt-3");
                    if (!(k < _length - 1))
                    {
                        layoutItemBuilder.AddAttribute(i++, "disabled", "true");
                        
                    }
                    
                    layoutItemBuilder.AddAttribute(i++, "style", "margin-left: auto; width:49%");
                    layoutItemBuilder.AddAttribute(i++, "Text", localizer.GetString(ResourceKeys.ButtonNext));
                    layoutItemBuilder.CloseComponent();

                    layoutItemBuilder.CloseElement(); //close div


                    layoutItemBuilder.OpenElement(i++, "div");
                    layoutItemBuilder.AddAttribute(i++, "class", "text-danger");
                    layoutItemBuilder.OpenComponent(i++, typeof(ValidationMessage<>).MakeGenericType(property.PropertyType));
                    layoutItemBuilder.AddAttribute(i++, "For", lambda);
                    layoutItemBuilder.CloseComponent();
                    layoutItemBuilder.CloseElement();


                    LogToBrowserConsole.LogToBC($"loop {k}, length: {_length}, formSteps: {FormSteps.Count} ");
                    k++;

                }
                layoutItemBuilder.OpenComponent<DxFormLayoutItem>(i++);
                layoutItemBuilder.AddAttribute(i++, "ColSpanMd", 12);
                layoutItemBuilder.AddAttribute(i++, "CssClass", "full-width justify-content-center");
                layoutItemBuilder.AddAttribute(i++, "Template", (RenderFragment<object>)((context) => ((editor) =>
                {
                    LogToBrowserConsole.LogToBC($"Submit button {CurrentStep}, {FormSteps.Count}");

                    editor.OpenElement(i++, "button");
                    editor.AddAttribute(i++, "type", "submit");
                    editor.AddAttribute(i++, "class", "btn btn-primary mt-3 w-100");
                    editor.AddAttribute(i++, "style", "margin: 0 auto");
                    if (CurrentStep < _length - 1)
                    {
                        editor.AddAttribute(i++, "disabled", "true");

                    }
                    //editor.AddAttribute(i++, "disabled", "true");

                    editor.OpenElement(i++, "span");
                    editor.AddAttribute(i++, "class", _spinnerClass);

                    editor.CloseElement();
                    editor.AddContent(i++, localizer.GetString(SubmitButtonText));
                    editor.CloseElement();

                    /*editor.OpenComponent<DxButton>(i++);
                    editor.AddAttribute(i++, "SubmitFormOnClick", true);
                    editor.AddAttribute(i++, "Text", SubmitButtonText);
                    editor.AddAttribute(i++, "IconClass", _spinnerClass);
                    editor.AddAttribute(i++, "CssClass", "btn btn-primary mt-3");
                    if (CurrentStep == propertyList.Length-1)
                    {
                        editor.AddAttribute(i++, "Visible", true);
                    }
                    else
                    {
                        editor.AddAttribute(i++, "Visible", false);
                    }                    
                    editor.CloseComponent();*/
                })));

                layoutItemBuilder.CloseComponent();
            }));
            formLayoutBuilder.CloseComponent();
        };

        private void SetPhoneNumber(PropertyInfo property, object Data, string str)
        {
            LogToBrowserConsole.LogToBC($"SetPhoneNumber called with {str}");
            property.SetValue(Data, str);
        }
    }

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class MinPasswordLengthAttribute : ValidationAttribute
    {
        int MinLength { get; }
        public MinPasswordLengthAttribute(int minLength, string errorMsg) : base(errorMsg)
        {
            MinLength = minLength;
        }

        public override bool IsValid(object value)
        {
            return ((string)value).Length >= MinLength;
        }
    }

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class EmailAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            return Regex.IsMatch((string)value, @"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*"
                                             + "@"
                                              + @"((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$");
        }
    }



}
