using Gremlin_BlazorServer.Data.DBClasses;
using Gremlin_BlazorServer.Data.EntityClasses;
using Gremlin_BlazorServer.Utilities;
using Microsoft.EntityFrameworkCore;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace Gremlin_BlazorServer.Services
{
	public class QuoteService
	{
		private readonly GremlinContext gremlinContext;
		public QuoteService(GremlinContext gC) => gremlinContext = gC;

		public void ConfigureServices(IServiceCollection services) => services.AddDbContext<GremlinContext>(ServiceLifetime.Scoped);

		public async Task<List<Quote>> GetAllQuotesAsync() => await gremlinContext.Quotes.ToListAsync();

		public async Task<List<LineItem>> GetLineItemsAsync(Quote quote) => await gremlinContext.LineItems.Where(lI => lI.QuoteId == quote.QuoteId).ToListAsync();

		public async Task<bool> InsertQuoteAsync(Quote quote)
		{
			try
			{
				_ = await gremlinContext.Quotes.AddAsync(quote);
				_ = await gremlinContext.SaveChangesAsync();
				return true;
			}
			catch (Exception exception)
			{
				Debug.WriteLine(exception.Message);
				return false;
			}
		}

		public async Task<Quote?> GetQuoteAsync(uint quoteId)
		{
			Quote? quote = await gremlinContext.Quotes.FirstOrDefaultAsync(quote => quote.QuoteId.Equals(quoteId));
			return quote;
		}

		public async Task<bool> UpdateQuoteAsync(Quote Quote)
		{
			_ = gremlinContext.Quotes.Update(Quote);
			_ = await gremlinContext.SaveChangesAsync();
			return true;
		}

		public async Task<bool> DeleteQuoteAsync(Quote Quote)
		{
			_ = gremlinContext.Remove(Quote);
			_ = await gremlinContext.SaveChangesAsync();
			return true;
		}

		//FromWindowsGremlin

		public Quote ReadLineItems(Quote quote, string clipboard)
		{
			quote = ResetTotals(quote);

			quote.LineItems = ReadLineItemsFromClipboard(clipboard);

			quote.QuoteContains3PP = DoesContains(quote, "3PP");
			quote.QuoteContainsRB = DoesContains(quote, "RB");

			quote.TotalListprice = GetTotal(quote, "TotalListprice");
			quote.TotalDiscount = GetTotal(quote, "AverageDiscount");
			quote.TotalNet = GetTotal(quote, "TotalNet");

			quote.ValidFor = CheckForAcademic(quote.Recipient);

			foreach (LineItem lineItem in quote.LineItems)
			{
				if (lineItem.OptionNumber != null)
				{
					// normale Gewährleistungsverlängerung
					if (lineItem.OptionNumber.StartsWith("8D"))
					{
						quote.Warranty = int.Parse(lineItem.OptionNumber.Last().ToString()) * 12;
					}

					//24 Monate Gewährleistung für Akademia
					if (lineItem.OptionNumber == "9EC")
					{
						quote.Warranty = 24;
					}

					//36 Monate Gewährleistung für Akademia
					if (lineItem.OptionNumber == "9CC")
					{
						quote.Warranty = 36;
					}
				}

				//AddToTotal
				quote.TotalListprice += lineItem.ListPrice;
				quote.TotalNet += lineItem.Total;
			}

			quote = CalculateTotals(quote);

			return quote;
		}

		private static List<LineItem> ReadLineItemsFromClipboard(string clipboard)
		{
			//Zeilen aufteilen
			IEnumerable<string> clipboardLines = clipboard.Split(Environment.NewLine.ToCharArray());
			List<string[]> clipboardList = new();

			foreach (string clipboardLine in clipboardLines)
			{
				if (clipboardLine != "")
				{
					//Nach Trennzeichen trennen
					string[] clipboardLineSplit = clipboardLine.Split('\t');
					clipboardList.Add(clipboardLineSplit);
				}
			}

			return ParseClipboardList(clipboardList);
		}

		private static List<LineItem> ParseClipboardList(List<string[]> lineItemStrings)
		{
			List<LineItem> lineItems = new();
			int countError = 0;
			int countEmpty = 0;

			foreach (string[] lineItemString in lineItemStrings)
			{
				//Anzahl an Spalten entspricht Clipboard
				if (lineItemString.Length == 19)
				{
					//Header ignorieren
					if (lineItemString[0] == "#")
					{
						countEmpty++;
						continue;
					}

					//Dateiinhalt in Klasse schreiben
					LineItem lineItem = new()
					{
						Position = ushort.Parse(lineItemString[0]),
						ProductNumber = lineItemString[1],
						OptionNumber = lineItemString[2],
						ProductLine = lineItemString[3],
						SapShortDescription = lineItemString[4],
						Amount = ushort.Parse(lineItemString[5]),
						ListPrice = decimal.Parse(lineItemString[6]),
						TotalDiscount = decimal.Parse(lineItemString[9]),
						NetPrice = decimal.Parse(lineItemString[10]),
						Total = decimal.Parse(lineItemString[11]),
						SalesDiscount = decimal.Parse(lineItemString[12]),
						ContractualDiscount = decimal.Parse(lineItemString[13]),
						PromotionalDiscount = decimal.Parse(lineItemString[14]),
						DemoDiscount = decimal.Parse(lineItemString[15])
					};

					lineItems.Add(lineItem);
				}
				else
				{
					Debug.WriteLine("Angebot konnte nicht eingelesen werden!");
					countError++;
					continue;
				}
			}
			return lineItems;
		}

		private static byte CheckForAcademic(Contact recipient)
		{
			return (byte)(recipient == null || recipient.Account.AccountType.AccountTypeCode == null ? 0 : recipient.Account.AccountType.AccountTypeCode.StartsWith("N") ? 90 : 60);
		}

		private static decimal GetFreight(decimal net, decimal freight)
		{
			decimal freightNet = net * freight / 100;
			return freightNet < 3000 ? freightNet : 3000;
		}

		private static Quote CalculateTotals(Quote quote)
		{
			quote.TotalFreightOnly = GetFreight(quote.TotalNet, quote.Freight);
			quote.TotalFreight = quote.TotalNet + quote.TotalFreightOnly;
			quote.TotalVAT = quote.TotalFreight * Convert.ToDecimal(quote.VAT) / 100;
			quote.TotalGross = quote.TotalFreight * (100 + Convert.ToDecimal(quote.VAT)) / 100;
			return quote;
		}

		private static Quote ResetTotals(Quote quote)
		{
			quote.TotalListprice = 0;
			quote.TotalNet = 0;
			quote.TotalFreightOnly = 0;
			quote.TotalFreight = 0;
			quote.TotalVAT = 0;
			quote.TotalGross = 0;

			return quote;
		}

		private static decimal GetTotal(Quote quote, string type)
		{
			decimal total = 0;

			foreach (LineItem lineItem in quote.LineItems)
			{
				switch (type)
				{
					case "TotalListprice":
						total += lineItem.ListPrice;
						break;
					case "TotalNet":
						total += lineItem.NetPrice;
						break;
					case "AverageDiscount":
						total += lineItem.TotalDiscount;
						break;
				}
			}

			if (type == "AverageDiscount" & quote.LineItems.Count != 0) { total /= quote.LineItems.Count; }

			return total;
		}

		private static bool DoesContains(Quote quote, string type)
		{
			foreach (LineItem lineItem in quote.LineItems)
			{
				switch (type)
				{
					case "3PP":
						if (lineItem.ProductLine == "3PP")
						{
							Debug.WriteLine($"Quote containts 3PP with ProductNumber {lineItem.ProductNumber}");
							return true;
						}
						break;
					case "RB":
						if (lineItem.ProductLine == "RB" & lineItem.ProductNumber != "R2005A")
						{
							Debug.WriteLine($"Quote containts RB with ProductNumber {lineItem.ProductNumber}");
							return true;
						}
						break;
				}
			}
			return false;
		}
	}
}