using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Design;
using FastReport.Editor.Syntax;
using InABox.Core;
using InABox.Wpf;
using Microsoft.Win32;

namespace InABox.DynamicGrid
{
    /// <summary>
    ///     Interaction logic for DynamicImportForm.xaml
    /// </summary>
    public partial class DynamicImportForm : ThemableWindow
    {
        private Type _entitytype;
        private ImportDefinition _importdefinition;
        private readonly Importer _importer;
        private bool bLoading;

        public DynamicImportForm(Importer importer)
        {
            _importer = importer;


            InitializeComponent();

            LoadData();

            CheckOKButton();
        }

        private void LoadData()
        {
            bLoading = true;

            _entitytype = CoreUtils.GetEntity(_importer.EntityName);

            _importdefinition = ImportFactory.Definitions.FirstOrDefault(x => x.Description.Equals(_importer.ImporterDescription));

            var _mappings = string.IsNullOrWhiteSpace(_importer.Definition)
                ? new List<ImportMapping>()
                : Serialization.Deserialize<List<ImportMapping>>(_importer.Definition);

            Name.Text = _importer.Description;
            Type.ItemsSource = ImportFactory.Definitions;
            Type.SelectedValue = _importdefinition;
            HasHeader.IsChecked = _importer.HasHeader;
            HeaderRow.Value = Math.Max(1, _importer.HeaderRows);
            ColumnWidths.Text = _importer.ColumnWidths;
            FileName.Text = _importer.FileName;

            var mappings = ImportFactory.ExtractMappings(_entitytype, ImportMappingType.Import);
            Mappings.Items.AddRange(mappings);
            var key = mappings.FirstOrDefault(x => x.Key.Equals(true));
            if (key != null)
            {
                key.Key = false;
                Mappings.UniqueCode = key.Property;
            }
            else
            {
                Mappings.UniqueCode = "";
            }

            foreach (var custom in _mappings)
            {
                var mapping = Mappings.Items.FirstOrDefault(x => string.Equals(x.Property, custom.Property));
                if (mapping != null)
                {
                    mapping.Key = custom.Key;
                    mapping.Field = custom.Field;
                    mapping.Constant = custom.Constant;
                    mapping.Lookup = custom.Lookup;
                }
            }

            ImportFieldGenerator.Fields = Array.Empty<string>();

            if (File.Exists(FileName.Text) && Type.SelectedValue as ImportDefinition != null)
                OpenFile(FileName.Text);
            else
                Mappings.Refresh(true, true);

            bLoading = false;
        }

        private void OpenFile(string filename)
        {
            if(Type.SelectedValue is not ImportDefinition definition)
            {
                return;
            }

            var importer = ImportFactory.Create(definition, _entitytype, _importer);

            try
            {
                using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
                {
                    if (importer.Open(stream) && importer.ReadHeader())
                    {
                        FileName.Text = filename;
                        ImportFieldGenerator.Fields = importer.Fields;
                        Mappings.Refresh(true, true);
                    }
                    else
                    {
                        MessageBox.Show(string.Format("{0} is not a valid file!", filename));
                    }
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(string.Format("Unable to open {0}!\n\n{1}", filename, e.Message));
            }
        }

        private void Open_Click(object sender, RoutedEventArgs e)
        {
            var type = Type.SelectedValue as ImportDefinition;
            if (type == null)
            {
                MessageBox.Show(string.Format("Type is unexpected: {0}", type));
                return;
            }

            var dlg = new OpenFileDialog();
            dlg.Filter = type.Filter;

            var sFile = FileName.Text;
            if (!string.IsNullOrWhiteSpace(sFile))
            {
                dlg.FileName = sFile;
                var sFolder = string.IsNullOrWhiteSpace(sFile) ? Path.GetDirectoryName(sFile) : "";
                if (!string.IsNullOrWhiteSpace(sFolder) && Directory.Exists(sFolder))
                    dlg.InitialDirectory = sFolder;
            }

            if (dlg.ShowDialog() == true)
                if (File.Exists(dlg.FileName) && Type.SelectedValue as ImportDefinition != null)
                    OpenFile(dlg.FileName);
            CheckOKButton();
        }

        private void HeaderRow_Checked(object sender, RoutedEventArgs e)
        {
            HeaderRowLabel.Visibility = HasHeader.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
            HeaderRow.Visibility = HasHeader.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
        }

        private void HeaderRow_ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!bLoading)
                _importer.HeaderRows = HeaderRow.Value.HasValue ? (int)HeaderRow.Value : 1;
            if (File.Exists(FileName.Text) && Type.SelectedValue as ImportDefinition != null)
                OpenFile(FileName.Text);
            CheckOKButton();
        }

        private void OpenTypeSettings()
        {
            if(Type.SelectedValue is not ImportDefinition definition)
            {
                return;
            }

            var tSettings = definition.Type.GetInterfaceDefinition(typeof(ISettingsImporter<>))?.GenericTypeArguments[0];
            if(tSettings is null)
            {
                return;
            }

            object? settings = null;
            if (!_importer.Settings.IsNullOrWhiteSpace())
            {
                settings = Serialization.Deserialize(tSettings, _importer.Settings);
            }
            settings ??= Activator.CreateInstance(tSettings)!;

            var grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicGrid<>), tSettings);
            if(grid.EditItems(new object[] { settings }))
            {
                _importer.Settings = Serialization.Serialize(settings);
            }
        }

        private void TypeSettings_Click(object sender, RoutedEventArgs e)
        {
            OpenTypeSettings();

            if (File.Exists(FileName.Text))
                OpenFile(FileName.Text);
        }

        private void Type_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!bLoading)
                _importdefinition = e.AddedItems.Count > 0 ? e.AddedItems[0] as ImportDefinition : null;
            Open.IsEnabled = _importdefinition != null;
            ColumnWidthsLabel.Visibility = _importdefinition != null && _importdefinition.Type == typeof(FixedWidthImporter<>)
                ? Visibility.Visible
                : Visibility.Hidden;
            ColumnWidths.Visibility = _importdefinition != null && _importdefinition.Type == typeof(FixedWidthImporter<>)
                ? Visibility.Visible
                : Visibility.Hidden;

            TypeSettings.IsEnabled = false;
            if (Type.SelectedValue is ImportDefinition definition)
            {
                if (definition.Type.HasInterface<ISettingsImporter>())
                {
                    TypeSettings.IsEnabled = true;
                    if (!bLoading)
                    {
                        OpenTypeSettings();
                    }
                }

                if (File.Exists(FileName.Text))
                    OpenFile(FileName.Text);
            }
            CheckOKButton();
        }

        private void HideBlank_Click(object sender, RoutedEventArgs e)
        {
            Mappings.HideBlank = !Mappings.HideBlank;
            HideBlank.Content = Mappings.HideBlank ? "Show All" : "Hide Blank";
            Mappings.Refresh(false, true);
        }

        private void Match_Click(object sender, RoutedEventArgs e)
        {
            Mappings.MatchFields();
        }

        private void Reset_Click(object sender, RoutedEventArgs e)
        {
            Mappings.Reset();
        }

        private void Cancel_Click(object sender, RoutedEventArgs e)
        {
            DialogResult = false;
        }

        private void OK_Click(object sender, RoutedEventArgs e)
        {
            var bOK = true;
            if (!Mappings.Items.Any(x => x.Key))
                bOK = MessageBox.Show(
                    "There are no keys defined - this import will ALWAYS create new items\ninstead of updating existing items.\n\nAre you sure you wish to do this?",
                    "No Keys Defined", MessageBoxButton.YesNo) == MessageBoxResult.Yes;
            if (bOK)
            {
                UnloadData();
                DialogResult = true;
            }
        }

        private void UnloadData()
        {
            _importer.EntityName = _entitytype.EntityName();
            _importer.ImporterDescription = _importdefinition != null ? _importdefinition.Description : "";
            _importer.Definition =
                Serialization.Serialize(Mappings.Items.Where(x => !string.IsNullOrWhiteSpace(x.Field) || !string.IsNullOrWhiteSpace(x.Constant)),
                    true);
            _importer.Description = Name.Text;
            _importer.FileName = FileName.Text;
        }

        private void Name_TextChanged(object sender, TextChangedEventArgs e)
        {
            CheckOKButton();
        }

        private void CheckOKButton()
        {
            OK.IsEnabled = !string.IsNullOrWhiteSpace(Name.Text) && _importdefinition != null && !string.IsNullOrWhiteSpace(FileName.Text);
        }

        private int[] ExtractColumnWidths()
        {
            if (string.IsNullOrWhiteSpace(ColumnWidths.Text))
                return new[] { 1024 };

            try
            {
                return ColumnWidths.Text.Split(',').Select(x => int.Parse(x)).ToArray();
            }
            catch
            {
                return new[] { 1024 };
            }
        }

        private void ColumnWidths_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (!bLoading)
                _importer.ColumnWidths = ColumnWidths.Text;
            if (File.Exists(FileName.Text) && Type.SelectedValue as ImportDefinition != null)
                OpenFile(FileName.Text);
        }

        private void EditScript_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(_importer.Script))
                _importer.Script =
                    "using System;\r\n" +
                    "using System.Collections.Generic;\r\n" +
                    "using System.Linq;\r\n" +
                    "using System.Runtime;\r\n" +
                    "using System.Windows;\r\n" +
                    "using System.Windows.Media;\r\n" +
                    "using InABox.Core;\r\n" +
                    "using Comal.Classes;\r\n" +
                    "\r\n" +
                    "public class Module\r\n" +
                    "{\r\n" +
                    "\r\n" +
                    "    public bool BeforeProcess(Dictionary<String,String> values)\r\n" +
                    "    {\r\n" +
                    "        return true;\r\n" +
                    "    }\r\n" +
                    "\r\n" +
                    "    public void AfterProcess(" + _entitytype.Name + " item, Dictionary<String,String> values)\r\n" +
                    "    {\r\n" +
                    "        return true;\r\n" +
                    "    }\r\n" +
                    "\r\n" +
                    "}";
            var editor = new ScriptEditorWindow(_importer.Script);
            if (editor.ShowDialog() == true) _importer.Script = editor.Script;
        }

        private void Save_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new SaveFileDialog();
            dlg.Filter = string.Format("{0} Import Files (*.{1}import)|*.{1}import", _entitytype.Name, _entitytype.Name.ToLower());
            dlg.FileName = string.Format("{0}.{1}import", Name.Text, _entitytype.Name.ToLower());
            if (dlg.ShowDialog() == true)
                try
                {
                    UnloadData();
                    var json = Serialization.Serialize(_importer, true);
                    File.WriteAllText(dlg.FileName, json);
                }
                catch (Exception e2)
                {
                    MessageBox.Show("Error saving import definition!\n\n" + e2.Message);
                }
        }

        private void Load_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new OpenFileDialog();
            dlg.Filter = string.Format("{0} Import Files (*.{1}import)|*.{1}import", _entitytype.Name, _entitytype.Name.ToLower());
            if (dlg.ShowDialog() == true)
            {
                Mappings.Items.Clear();
                var json = File.ReadAllText(dlg.FileName);
                var id = _importer.ID;
                var entityid = _importer.EntityID;
                Serialization.DeserializeInto(json, _importer);
                _importer.SetObserving(false);
                _importer.ID = id;
                _importer.SetObserving(true);
                _importer.EntityID = entityid;
                LoadData();
            }

            CheckOKButton();
        }
    }
}