using System;
using System.Collections.Generic;
using System.Data.Entity.Validation;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using MileageTraker.Web.Attributes;
using MileageTraker.Web.DAL;
using MileageTraker.Web.Utility;
using MileageTraker.Web.ViewModels;
using MileageTraker.Web.ViewModels.Log;
using MileageTraker.Web.ViewModels.Vehicle;

namespace MileageTraker.Web.Controllers
{
	[Authorize(Roles = "Administrator, Developer")]
	public class LogController : ControllerBase
    {
        public ViewResult Index(LogQueryViewModel query)
        {
			var validLogYearMonths = DataService.GetValidLogMonths();

			if (!validLogYearMonths.Any()) // this means no logs in DB
				return View("Empty");

			// default parameter processing
			if (!query.HasParameters())
			{
				query.Year = validLogYearMonths.First().Year;
				query.Month = validLogYearMonths.First().Month;
				query.MonthRange = 1;
			}
			if (query.Year.HasValue && !query.Month.HasValue)
			{
				var validLogMonths = validLogYearMonths.Where(dt => dt.Year == query.Year).ToList();
				query.Month = validLogMonths.Min(dt => dt.Month);
				query.MonthRange = validLogMonths.Max(dt => dt.Month) - query.Month + 1;
			}

	        var logs = DataService.GetLogs();
			
			var filteredLogs = 
				(from log in DataService.GetLogIndexViewModels(DataService.FilterLogs(logs, query))
				orderby log.Created descending 
				select log).ToList();

			var viewModel = new LogResultsViewModel(filteredLogs, query, CustomExtensions.YearMonthList(validLogYearMonths));

			Session.Add("LogPage", Request.Url.PathAndQuery);

			return View(viewModel);
        }

		[ActionLog]
		public ActionResult Export(LogQueryViewModel query)
		{
			var logs = DataService.GetLogs();
			var filteredLogs =
				DataService.GetLogIndexViewModels(
					DataService.FilterLogs(logs, query)
				           ).ToList();

			var name = string.Format("MileageLogs_{0}", query);

			var export = ExcelWriter<LogIndexViewModel>.WriteXls(filteredLogs, name, name);
			return File(export, "application/ms-excel", name + ".xls");
		}

		public ViewResult MonthlyVehicleMileage(LogQueryViewModel query)
		{
			var items = DataService.GetMonthlyVehicleMileageItems(query);

			var report = new VehicleMileageViewModel (items, query);

			return View(report);
		}

		public ActionResult MonthlyDriverMileage(LogQueryViewModel query)
		{
			var items = DataService.GetMonthlyDriverMileageItems(query);
			items = items.OrderBy(i => i.DriverName);

			var report = new DriverMileageViewModel(items, query);

			return View(report);
		}

		public ViewResult Details(int id)
        {
			var log = DataService.GetLog(id);
			var viewModel = new LogViewModel(log)
				{
					Purpose = {Available = GetPurposeTypesSelectList()}
				};
			return View(viewModel);
        }
		
		public ActionResult PreviousDetails(int id)
        {
			var log = DataService.GetLog(id);
			int logId;
			if (log.VehiclePreviousLog != null)
			{
				logId = log.VehiclePreviousLog.LogId;
			}
			else
			{
				logId = id;
				TempData["StatusMessage"] = "This is the first log for this vehicle";
			}
			return RedirectToAction("Details", new {id = logId});
        }

		public ActionResult NextDetails(int id)
		{
			var nextLog = DataService.GetNextLog(id);
			int logId;
			if (nextLog != null)
			{
				logId = nextLog.LogId;
			}
			else
			{
				logId = id;
				TempData["StatusMessage"] = "This is the most recent log for this vehicle";
			}
			return RedirectToAction("Details", new { id = logId });
		}

		[HttpGet]
		public ActionResult Create(string vehicleId)
		{
			var viewModel = new LogViewModel
				{
					Date = DateTime.Today,
					VehicleId = vehicleId,
					Purpose = {Available = GetPurposeTypesSelectList()},
				};

			return View(viewModel);
		} 

        [HttpPost]
		[ActionLog]
		public ActionResult Create(LogViewModel viewModel)
        {
			if (ModelState.IsValid)
			{
				var log = viewModel.GetLog();
				log.User = DataService.FindUserByFullName(viewModel.UserFullName);
				log.Source = HttpContext.Request.Url.AbsolutePath;
				log.UserHostAddress = HttpContext.Request.UserHostAddress;
				log.UserAgent = HttpContext.Request.UserAgent;

				if (viewModel.Purpose != null)
					log.Purpose =
						DataService.GetPurposeTypes()
						.First(pt => pt.PurposeTypeId == viewModel.Purpose.Selected);

				DataService.AddLog(log);

				TempData["StatusMessage"] = "Log created";

				if (TempData.ContainsKey("FuelLogId"))
				{
					// update fuel log
					var fuelLogId = (int)TempData["FuelLogId"];
					var fuelLog = DataService.GetFuelLog(fuelLogId);
					fuelLog.Log = log;
					DataService.UpdateFuelLog(fuelLog);

					// return to the fuel log match page
					TempData["StatusMessage"] = "Log created and matched to the fuel log";
					return RedirectToAction("Match", "FuelLog", new {id = fuelLogId});
				}

				return RedirectToAction("Index");  
            }

	        viewModel.Purpose.Available = GetPurposeTypesSelectList();
            return View(viewModel);
        }

		public ActionResult Edit(int id)
        {
			var log = DataService.GetLog(id);
			var viewModel = new LogViewModel(log)
				{
					Purpose = {Available = GetPurposeTypesSelectList()}
				};
			return View(viewModel);
        }

		[HttpPost]
		[ActionLog]
		public ActionResult Edit(LogViewModel viewModel)
        {
        	if (ModelState.IsValid)
            {
	            var log = DataService.GetLog(viewModel.LogId);
				viewModel.SetProperties(log);
				log.User = DataService.FindUserByFullName(viewModel.UserFullName);

				if (viewModel.Purpose != null)
					log.Purpose =
						DataService.GetPurposeTypes()
						.First(pt => pt.PurposeTypeId == viewModel.Purpose.Selected);
				
				DataService.UpdateLog(log);

	            TempData["StatusMessage"] = "Log updated";
                return RedirectToAction("Details", new{id = log.LogId});
            }

			viewModel.Purpose.Available = GetPurposeTypesSelectList();
			return View(viewModel);
        }

		public ActionResult Delete(int id)
        {
        	var log = DataService.GetLog(id);
			var viewModel = new LogViewModel(log)
				{
					Purpose = {Available = GetPurposeTypesSelectList()}
				};
			return View(viewModel);
        }

		[HttpPost, ActionName("Delete")]
		[ActionLog]
		public ActionResult DeleteConfirmed(int id)
        {            
            DataService.DeleteLog(id);

			TempData["StatusMessage"] = "Log deleted";
			if (Session["LogPage"] != null)
				return Redirect((string) Session["LogPage"]);
            return RedirectToAction("Index");
        }

		public PartialViewResult DetailsPrevious(int id)
		{
			var log = DataService.GetLog(id);
			return PartialView(new LogPartialDetails(log));
		}
		
		#region Import

		public ActionResult ImportUpload()
		{
			return View();
		}

		[HttpPost]
		public ActionResult Import(ImportUploadViewModel viewModel)
		{
			if (ModelState.IsValid && viewModel.File != null && viewModel.File.ContentLength > 0)
			{
				var user = DataService.FindUserByFullName(viewModel.UserName) ??
						   DataService.FindUserByUsername(viewModel.UserName);
				if (user == null)
				{
					TempData["StatusMessage"] = "User " + viewModel.UserName + " not found";
					TempData["StatusMessage-Type"] = "alert-error";
				}
				else
				{
					ViewData["UserFullName"] = user.FullName;

					var fileName = Path.GetFileName(viewModel.File.FileName);
					var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
					viewModel.File.SaveAs(path);

					try
					{
						var logImports = LogImporter.Import(path);
						// todo: delete file?
						return View(logImports.ToList());
					}
					catch (OutOfMemoryException)
					{
						TempData["StatusMessage"] = "Problem reading excel document - please verify that the document is in Excel 97-2003 Workbook (.xls) format";
						TempData["StatusMessage-Type"] = "alert-error";
					}
					catch (Exception ex)
					{
						TempData["StatusMessage"] = "Problem reading excel document: " + ex.Message;
						TempData["StatusMessage-Type"] = "alert-error";
					}
				}
			}
			return RedirectToAction("ImportUpload");
		}

		enum ImportStatus
		{
			Success,
			Failure,
			Duplicate
		}

		[HttpPost]
		[ActionLog]
		public ActionResult ImportCreate(ImportLogViewModel viewModel, string userFullName)
		{
			var buttonAttributes = new Dictionary<string, object> { { "class", "btn btn-mini" }, { "target", "_blank" } };

			try
			{
				// verify purpose
				var purposeType = DataService.FindPurposeType(viewModel.Purpose);
				if (purposeType == null)
				{
					return Json(new
					{
						Status = ImportStatus.Failure.ToString(),
						Message = "Invalid Purpose: " + viewModel.Purpose,
						Action = GetFixLink(viewModel)
					}, JsonRequestBehavior.AllowGet);
				}

				if (!ModelState.IsValid)
				{
					var errorList = GetModelStateErrorList(ModelState);
					// remove the object name from the errors
					errorList = errorList.Select(s => s.Replace("viewModel.", ""));

					return Json(new
					{
						Status = ImportStatus.Failure.ToString(),
						Message = string.Join(", ", errorList),
						Action = GetFixLink(viewModel)
					}, JsonRequestBehavior.AllowGet);
				}

				var log = viewModel.GetLog();
				log.User = DataService.FindUserByFullName(userFullName);
				log.Source = HttpContext.Request.Url.AbsolutePath;
				log.UserHostAddress = HttpContext.Request.UserHostAddress;
				log.UserAgent = HttpContext.Request.UserAgent;
				log.Purpose = purposeType;

				// check duplicate
				var duplicateLogs = DataService.GetDuplicateLogs(log).ToList();
				if (duplicateLogs.Any())
				{
					var link = HtmlHelper.GenerateLink(
						ControllerContext.RequestContext, RouteTable.Routes,
						"Details", "Default", "Details", "Log",
						new RouteValueDictionary(new{ id = duplicateLogs.First().LogId }),
						buttonAttributes);
					return Json(new
						{
							Status = ImportStatus.Duplicate.ToString(),
							Message = "This log has been previously entered",
							Action = link
						}, JsonRequestBehavior.AllowGet);
				}
				// verify vehicle exists
				if (DataService.GetVehicle(log.VehicleId) == null)
				{
					return Json(new
					{
						Status = ImportStatus.Failure.ToString(),
						Message = "Vehile with supplied ID does not exist",
						Action = GetFixLink(viewModel)
					}, JsonRequestBehavior.AllowGet);
				}
				// verify city exists
				if (DataService.FindCityByName(log.CityName) == null)
				{
					return Json(new
					{
						Status = ImportStatus.Failure.ToString(),
						Message = "City must exist in system",
						Action = GetFixLink(viewModel)
					}, JsonRequestBehavior.AllowGet);
				}
				
				// SAVE IT!
				DataService.AddLog(log);

				var successLink = HtmlHelper.GenerateLink(
					ControllerContext.RequestContext, RouteTable.Routes,
					"Details", "Default", "Details", "Log",
					new RouteValueDictionary(new { id = log.LogId }),
					buttonAttributes);

				return Json(new
					{
						Status = ImportStatus.Success.ToString(),
						Action = successLink
					}, JsonRequestBehavior.AllowGet);
			}
			catch (DbEntityValidationException ex)
			{
				// Retrieve the error messages as a list of strings.
				var errorMessages = ex.EntityValidationErrors
						.SelectMany(x => x.ValidationErrors)
						.Select(x => x.ErrorMessage);

				// Join the list to a single string.
				var fullErrorMessage = string.Join(", ", errorMessages);

				return Json(new
					{
						Status = ImportStatus.Failure.ToString(), 
						Message = fullErrorMessage,
						Action = GetFixLink(viewModel)
					},
					JsonRequestBehavior.AllowGet);
			}
			catch (Exception ex)
			{
				var message = ex.Message;
				return Json(new
					{
						Status = ImportStatus.Failure.ToString(),
						Message = message,
						Action = GetFixLink(viewModel)
					}, JsonRequestBehavior.AllowGet);
			}
		}

		private string GetFixLink(ImportLogViewModel viewModel)
		{
			try
			{
				viewModel.GetLogViewModel();
			}
			catch
			{
				return string.Empty;
			}

			return RenderRazorViewToString("ImportFix", viewModel);
		}

		[HttpPost]
		public ActionResult ImportFix(ImportLogViewModel viewModel)
		{
			var logViewModel = viewModel.GetLogViewModel();
			logViewModel.Purpose.Available = GetPurposeTypesSelectList();
			return View("Create", logViewModel);
		}

		public ActionResult ImportTemplate()
		{
			var template = LogImportTemplateWriter.Write();
			return File(template, "application/ms-excel", "MileageTraker_ImportTemplate.xls");
		}

		#endregion
	}
}