using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using TransportGuideContracts.BindingModels;
using TransportGuideContracts.SearchModels;
using TransportGuideContracts.StoragesContracts;
using TransportGuideContracts.ViewModels;
using TransportGuideDatabaseImplements.Models;

namespace TransportGuideDatabaseImplements.Implements
{
	public class RouteStorage : IRouteStorage
	{
		public RouteViewModel? Delete(RouteBindingModel model)
		{
			using var context = new TransportGuideDB();

			var element = context.Routes.Include(x => x.Stops).Include(x => x.TransportType).FirstOrDefault(rec => rec.Id == model.Id);

			if (element != null)
			{
				context.Routes.Remove(element);
				context.SaveChanges();
				return element.GetViewModel;
			}

			return null;
		}

		public RouteViewModel? GetElement(RouteSearchModel model)
		{
			if (string.IsNullOrEmpty(model.Name) && !model.Id.HasValue)
			{
				return null;
			}

			using var context = new TransportGuideDB();

			return context.Routes.Include(x => x.Stops).ThenInclude(x => x.Stop).FirstOrDefault(x => (!string.IsNullOrEmpty(model.Name) && x.Name == model.Name) || (model.Id.HasValue && x.Id == model.Id))?.GetViewModel;
		}

		public List<RouteViewModel> GetFilteredList(RouteSearchModel model)
		{
			if (string.IsNullOrEmpty(model.Name))
			{
				return new();
			}

			using var context = new TransportGuideDB();

			return context.Routes.Include(x => x.Stops).ThenInclude(x => x.Stop).Where(x => x.Name.Contains(model.Name)).ToList().Select(x => x.GetViewModel).ToList();
		}

		public List<RouteViewModel> GetFullList()
		{
			using var context = new TransportGuideDB();

			return context.Routes.Include(x => x.TransportType).Include(x => x.Stops).ThenInclude(x => x.Stop).ToList().Select(x => x.GetViewModel).ToList();
		}

		public RouteViewModel? Insert(RouteBindingModel model)
		{
			using var context = new TransportGuideDB();

			var newRoute = Route.Create(context, model);

			if (newRoute == null)
			{
				return null;
			}

			context.Routes.Add(newRoute);
			context.SaveChanges();

			return newRoute.GetViewModel;
		}

		public string TestInsertList(int num)
		{
			var context = new TransportGuideDB();
			Stopwatch stopwatch = new();
			Random rnd = new Random();
			long[] res = new long[num * 2];
			List<TransportTypeViewModel> _types = context.TransportTypes.ToList().Select(x => x.GetViewModel).ToList();


			for (int i = 0; i < num; ++i)
			{
				int rndId = rnd.Next();
				var model = new StopBindingModel
				{
					Id = rndId,
					Name = "Stop" + rndId.ToString(),
				};
				context.Stops.Add(Stop.Create(model));
				stopwatch.Start();
				context.SaveChanges();
				stopwatch.Stop();
				res[i] = stopwatch.ElapsedMilliseconds;
			}
			List<StopViewModel> _stops = context.Stops.Select(x => x.GetViewModel).ToList();

			for (int i = 0; i < num; ++i)
			{
				int rndId = rnd.Next();
				var model = new RouteBindingModel
				{
					Id = rndId,
					Name = "Route" + rndId.ToString(),
					IP = "IP" + rndId.ToString(),
					TransportTypeId = _types[rnd.Next(_types.Count)].Id
				};
				context.Routes.Add(Route.Create(context, model));
				stopwatch.Start();
				context.SaveChanges();
				stopwatch.Stop();
				res[i] = stopwatch.ElapsedMilliseconds;
			}

			long sum = 0;
			for (int i = 0; i < num; i++)
			{
				sum += res[i];
			}
			int result = Convert.ToInt32(sum / num);
			return result.ToString();
		}

		public string TestReadList(int num)
		{
			var context = new TransportGuideDB();
			Stopwatch stopwatch = new();

			long[] res = new long[num];

			for (int i = 0; i < num; i++)
			{

				stopwatch.Start();
				List<RouteViewModel> list = context.Routes.Include(x => x.TransportType).Include(x => x.Stops).ThenInclude(x => x.Stop).ToList().Select(x => x.GetViewModel).ToList();
				stopwatch.Stop();
				res[i] = stopwatch.ElapsedMilliseconds;
			}

			long sum = 0;
			for (int i = 0; i < num; i++)
			{
				sum += res[i];
			}
			int result = Convert.ToInt32(sum / num);
			return result.ToString();
		}

		public RouteViewModel? Update(RouteBindingModel model)
		{
			using var context = new TransportGuideDB();

			using var transaction = context.Database.BeginTransaction();

			try
			{
				var route = context.Routes.Include(x => x.TransportType).FirstOrDefault(rec => rec.Id == model.Id);

				if (route == null)
				{
					return null;
				}

				route.Update(model);
				context.SaveChanges();
				route.UpdateStops(context, model);
				transaction.Commit();

				return route.GetViewModel;
			}
			catch
			{
				transaction.Rollback();

				throw;
			}
		}
	}
}