using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FileContextCore.Infrastructure.Internal;
using FileContextCore.Serializer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using OfficeOpenXml;

namespace FileContextCore.StoreManager
{
    public class EXCELStoreManager : IStoreManager
    {
        private IEntityType _entityType;
        private string[] _propertyKeys;
        private Type[] _typeList;
        private int[] _propertyColumnIndices;
        private IFileContextScopedOptions _options;
        private object _keyValueFactory;

        private static Dictionary<string, ExcelPackage> _packages = new Dictionary<string, ExcelPackage>();
        private ExcelPackage _package;
        private ExcelWorksheet _worksheet;

        FileInfo GetFilePath()
        {
            string folder = string.IsNullOrEmpty(_options.Location)
                ? Path.Combine(AppContext.BaseDirectory, "appdata")
                : _options.Location;

            if (!Directory.Exists(folder))
            {
                Directory.CreateDirectory(folder);
            }

            return new FileInfo(Path.Combine(folder, $"{_options.DatabaseName}.xlsx"));
        }

        public void Initialize(IFileContextScopedOptions options, IEntityType entityType, object keyValueFactory)
        {
            _options = options;
            _entityType = entityType;
            _propertyKeys = entityType.GetProperties().Select(p => p.GetColumnName()).ToArray();
            _propertyColumnIndices = new int[_propertyKeys.Length];
            _typeList = entityType.GetProperties().Select(p => p.GetValueConverter()?.ProviderClrType ?? p.ClrType)
                .ToArray();
            _keyValueFactory = keyValueFactory;

            if (!_packages.ContainsKey(options.DatabaseName))
            {
                if (!String.IsNullOrEmpty(options.Password))
                {
                    _package = new ExcelPackage(GetFilePath(), options.Password);
                    _packages.Add(options.DatabaseName, _package);
                }
                else
                {
                    _package = new ExcelPackage(GetFilePath());
                    _packages.Add(options.DatabaseName, _package);
                }
            }
            else
            {
                _package = _packages[options.DatabaseName];
            }
            
            string name = entityType.GetTableName();
            _worksheet = _package.Workbook.Worksheets[name];

            if (_worksheet == null)
            {
                _worksheet = _package.Workbook.Worksheets.Add(name);
                _worksheet.Column(1).AutoFit();

                for (int i = 0; i < _propertyKeys.Length; i++)
                {
                    _worksheet.Cells[1, i + 1].Value = _propertyKeys[i];
                    _propertyColumnIndices[i] = i + 1;
                    _worksheet.Column(i + 1).AutoFit();
                }

                _worksheet.View.FreezePanes(2, 1);

                if (!String.IsNullOrEmpty(options.Password))
                {
                    _package.Save(options.Password);
                }
                else
                {
                    _package.Save();
                }
            }
            else
            {
                for (int i = 0; i < _propertyKeys.Length; i++)
                {
                    for (int x = 0; x < _worksheet.Dimension.Columns; x++)
                    {
                        string val = _worksheet.Cells[1, x + 1].GetValue<string>();
                        if (_propertyKeys[i].Equals(val, StringComparison.InvariantCultureIgnoreCase))
                        {
                            _propertyColumnIndices[i] = x + 1;
                            break;
                        }
                    }
                }
            }
        }

        public Dictionary<TKey, object[]> Deserialize<TKey>(Dictionary<TKey, object[]> newList)
        {
            for (int y = 2; y <= _worksheet.Dimension.Rows; y++)
            {
                List<object> value = new List<object>();

                for (int x = 0; x < _propertyKeys.Length; x++)
                {
                    object val = _worksheet.Cells[y, _propertyColumnIndices[x]].GetValue<string>()
                        .Deserialize(_typeList[x]);
                    value.Add(val);
                }

                TKey key = SerializerHelper.GetKey<TKey>(_keyValueFactory, _entityType, propertyName =>
                    _worksheet.Cells[y, _propertyColumnIndices[_propertyKeys.IndexOf(propertyName)]]
                        .GetValue<string>());

                newList.Add(key, value.ToArray());
            }

            return newList;
        }

        public void Serialize<TKey>(Dictionary<TKey, object[]> list)
        {
            FillRows(list);

            for (int x = 0; x < _worksheet.Dimension.Columns; x++)
            {
                _worksheet.Column(x + 1).AutoFit();
            }

            DeleteUnusedRows(list.Count + 1);

            if (!String.IsNullOrEmpty(_options.Password))
            {
                _package.Save(_options.Password);
            }
            else
            {
                _package.Save();
            }
        }

        private void FillRows<TKey>(Dictionary<TKey, object[]> list)
        {
            int y = 2;

            foreach (KeyValuePair<TKey, object[]> val in list)
            {
                for (int x = 0; x < _propertyKeys.Length; x++)
                {
                    _worksheet.SetValue(y, x + 1, val.Value[x].Serialize());
                }

                y++;
            }
        }

        private void DeleteUnusedRows(int lastRow)
        {
            if (_worksheet.Dimension.Rows >= lastRow)
            {
                int count = _worksheet.Dimension.Rows - lastRow;
                if (count > 0)
                {
                    _worksheet.DeleteRow(lastRow + 1, count);
                }
            }
        }
    }
}