using ExcelComponents.Models;
using OfficeOpenXml;
using OfficeOpenXml.Drawing.Chart;
using OfficeOpenXml.Style;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExcelComponents
{
	public class ExcelBuilder
	{
		private ExcelPackage? package = null;
		private ExcelWorksheet? worksheet = null;
		public ExcelBuilder()
		{
			ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
		}
		private void SetStyle(int row, int col, ExcelCellStyle style)
		{
			if (worksheet == null)
			{
				return;
			}
			ExcelStyle excelStyle = worksheet.Cells[row, col].Style;
			excelStyle.WrapText = style.WrapText;
			excelStyle.HorizontalAlignment = style.HorizontalAlignment;
			excelStyle.VerticalAlignment = style.VerticalAlignment;
			excelStyle.Border.Top.Style = style.TopBorderStyle;
			excelStyle.Border.Bottom.Style = style.BottomBorderStyle;
			excelStyle.Border.Left.Style = style.LeftBorderStyle;
			excelStyle.Border.Right.Style = style.RightBorderStyle;
		}
		private void SetStyle(int row, int col, int endRow, int endCol,  ExcelCellStyle style)
		{
			if (worksheet == null)
			{
				return;
			}
			ExcelStyle excelStyle = worksheet.Cells[row, col, endRow, endCol].Style;
			excelStyle.WrapText = style.WrapText;
			excelStyle.HorizontalAlignment = style.HorizontalAlignment;
			excelStyle.VerticalAlignment = style.VerticalAlignment;
			excelStyle.Border.Top.Style = style.TopBorderStyle;
			excelStyle.Border.Bottom.Style = style.BottomBorderStyle;
			excelStyle.Border.Left.Style = style.LeftBorderStyle;
			excelStyle.Border.Right.Style = style.RightBorderStyle;
		}

		private void MergeCells(int row, int col, int endRow, int endCol, ExcelCellStyle? style = null)
		{
			if(worksheet == null) { return; }
			worksheet.Cells[row, col, endRow, endCol].Merge = true;
			style ??= new();
			SetStyle(row, col, endRow, endCol, style);
		}
		public void ConfigureColumns(double[] columnWidth)
		{
			if(worksheet == null) 
			{
				return; 
			}
			for(int i = 0; i < columnWidth.Length; i++) 
			{
				worksheet.Column(i+1).Width = columnWidth[i];
			}
		}
		public void CreateDocument()
		{
			package = new();
			worksheet = package.Workbook.Worksheets.Add("First list");
		}
		public void SaveDocument(string filePath)
		{
			package?.SaveAs(filePath);
		}
		public void InsertText(int row, int column, string str,ExcelCellStyle? style = null) 
		{
			if(worksheet == null)
			{
				return;
			}
			worksheet.Cells[row, column].Value = str;
			style ??= new();
			SetStyle(row, column, style);
		}
		private static string GetPropertyValue<T>(string propertyName, T obj)
		{
			Type type = typeof(T);
			string value = type.GetProperty(propertyName)!.GetValue(obj, null).ToString()!;
			return value;
		}
		public void InsertTable<T>(Dictionary<int, int> merge, string[] headers, string[] props, T[] data )
		{
			if(worksheet == null)
			{
				return; 
			}
			ExcelCellStyle style = new()
			{
				NoBorder = false,
				WrapText = true,
			};

			int rowInd = 2;
			int colStart = 1;
			int colInd = colStart;
			int mergeskip = 0;

			foreach(string header in headers){
				InsertText(rowInd, colInd, header,style);
				if (mergeskip == 0)
				{
					if (!merge.ContainsKey(colInd - colStart))
					{
						MergeCells(rowInd, colInd, rowInd + 1, colInd, style);
						colInd++;
					}
					else
					{
						int colEnd = colInd + merge[colInd - colStart] - 1;
						MergeCells(rowInd, colInd, rowInd, colEnd, style);
						rowInd = 3;
						mergeskip = merge[colInd - colStart];
					}
				}
				else
				{
					mergeskip--;
					colInd++;
					if(mergeskip == 0)
					{
						rowInd = 2;
					}
				}
			}
			rowInd++;
			for(int i = 0; i < data.Length; i++)
			{
				colInd = colStart;
				rowInd++;
				foreach(string prop in props)
				{
					string value = GetPropertyValue(prop, data[i]);
					InsertText(rowInd, colInd, value, style);
					colInd++;
				}
			}
		}
		public void InsertBigText(string[] text, ExcelCellStyle? style = null)
		{
			if (worksheet == null)
			{
				return;
			}
			style ??= new() { HorizontalAlignment = ExcelHorizontalAlignment.Left};
			for (int i = 0; i < text.Length; i++)
			{
				worksheet.Cells[i + 2, 1].Value = text[i];
				MergeCells(i + 2, 1, i + 2, text[i].Length / 7 + 1, style);
			}
		}
		public void InsertLinearChart(string chartTitle, List<(string, double[])> data, string[] labels, ChartLegendPosition legendPosition)
		{
			if(worksheet == null) { return; }
			ExcelChart linearchar = (ExcelLineChart)worksheet.Drawings.AddChart("Linear Chart", eChartType.Line);
			linearchar.Title.Text = chartTitle;
			foreach(var item in data)
			{
				linearchar.Series.Add(ArrayToString(item.Item2)).Header = item.Item1;
			}
			linearchar.Series[0].XSeries = ArrayToString(labels);
			linearchar.Legend.Position = GetLegendPosition(legendPosition);
			linearchar.SetSize(500, 500);
			linearchar.SetPosition(1, 0, 0, 0);
			}
		private static string ArrayToString<T>(T[] data)
		{
			StringBuilder builder = new StringBuilder("{");
			for(int i = 0; i < data.Length; i++)
			{
				if (typeof(T) == typeof(string))
				{
					builder.Append($"\"{data[i]}\"");
				}
				else if (typeof(T) == typeof(double))
				{
					builder.Append(data[i].ToString().Replace(",", "."));
				}
				else
				{
					builder.Append(data[i].ToString());
				}
				if(i == data.Length - 1)
				{
					builder.Append('}');
				}
				else
				{
					builder.Append(',');
				}
			}
			return builder.ToString();
		}
		private static eLegendPosition GetLegendPosition(ChartLegendPosition legendPosition)
		{
			return legendPosition switch
			{
				ChartLegendPosition.Top => eLegendPosition.Top,
				ChartLegendPosition.Bottom => eLegendPosition.Bottom,
				ChartLegendPosition.Left => eLegendPosition.Left,
				ChartLegendPosition.Right => eLegendPosition.Right,
				_ => eLegendPosition.Right
			};
		}
	}
}