using System;
using System.Collections.Generic;
using System.Data.Entity.Validation;
using System.IO;
using System.Linq;
using System.Security;
using System.Web.Mvc;
using System.Web.Routing;
using MileageTraker.Web.Attributes;
using MileageTraker.Web.DAL;
using MileageTraker.Web.Models;
using MileageTraker.Web.ViewModels;
using MileageTraker.Web.ViewModels.CreateLog;

namespace MileageTraker.Web.Controllers
{
	[Authorize(Roles = "Driver, Administrator, Developer")]
	public class CreateLogController : ControllerBase
	{
		private const string CookeNameVehicleid = "mr_vehicleId";
		private const string CookieKeyLogtype = "mr_logType";

		public ViewResult Index()
		{
			var createLogModel = NewCreateLogModel();

			return View(createLogModel);
		}

		[HttpPost]
		public ActionResult Index(CreateLogViewModel model)
		{
			if (ModelState.IsValid)
			{
				var confirmCreateLogViewModel = new ConfirmCreateLogViewModel(model);
				confirmCreateLogViewModel.Purpose.Available = GetPurposeTypesSelectList();
				return View("Confirm", confirmCreateLogViewModel);
			}

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

		[LogOwnerAuthorize]
		public ActionResult EditPast(int id)
		{
			var log = DataService.GetLog(id);
			var viewModel = new EditPastViewModel(log)
			{
				Purpose = { Available = GetPurposeTypesSelectList() }
			};
			return View(viewModel);
		}

		[HttpPost]
		[ActionLog]
		public ActionResult EditPast(EditPastViewModel viewModel)
		{
			if (ModelState.IsValid)
			{
				var log = DataService.GetLog(viewModel.LogId);
				if (log.User.Username != User.Identity.Name)
					throw new SecurityException();
				viewModel.SetProperties(log);

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

				DataService.UpdateLog(log);

				TempData["StatusMessage-Type"] = "alert-success";
				TempData["StatusMessage"] =
					@"You've successfully updated an entry
					traveling to <strong>" + viewModel.CityName + @"</strong> 
					for <strong>" + log.Purpose.Purpose + @"</strong> 
					on <strong>" + viewModel.Date.ToShortDateString() + @"</strong>
					in Vehicle Id <strong>" + viewModel.VehicleId + @"</strong>
					ending in <strong>" + viewModel.EndOdometer + @"</strong>
					miles on the odometer.";
				
				return RedirectToAction("Index");
			}

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

		[HttpParamAction]
		[HttpPost]
		public ViewResult Edit(CreateLogViewModel model)
		{
			model.Purpose.Available = GetPurposeTypesSelectList();
			return View("Index", model);
		}

		[HttpParamAction]
		[HttpPost]
		[ActionLog]
		public ActionResult Confirm(CreateLogViewModel model)
		{
			if (ModelState.IsValid)
			{
				SetCookieValue(CookieKeyLogtype, model.LogType.Enum.ToString());
				SetCookieValue(CookeNameVehicleid, model.VehicleId);

				// Save the new log
				var log = model.GetLog();
				log.User = DataService.FindUserByUsername(User.Identity.Name);
				log.Source = HttpContext.Request.Url.AbsolutePath;
				log.UserHostAddress = HttpContext.Request.UserHostAddress;
				log.UserAgent = HttpContext.Request.UserAgent;

				log.Purpose = 
					DataService.GetPurposeTypes()
					.First(pt => pt.PurposeTypeId == model.Purpose.Selected);

				DataService.AddLog(log);
				var editLink = Url.Action("EditPast", "CreateLog", new {id = log.LogId});
				
				TempData["StatusMessage-Type"] = "alert-success";
				TempData["StatusMessage"] =
					@"You've successfully created an entry
					traveling to <strong>" + model.CityName + @"</strong> 
					for <strong>" + log.Purpose.Purpose + @"</strong> 
					on <strong>" + model.Date.ToShortDateString() + @"</strong>
					in Vehicle Id <strong>" + model.VehicleId + @"</strong>
					ending in <strong>" + model.EndOdometer + @"</strong>
					miles on the odometer. " +
					@"<a href='" + editLink + @"' class='btn btn-mini btn-success'>Edit</a>";
				return RedirectToAction("Index");
			}

			model.Purpose.Available = GetPurposeTypesSelectList();
			return View("Index", model);
		}

		public PartialViewResult RecentLogs()
		{
			var username = User.Identity.Name;
			var user = DataService.FindUserByUsername(username);
			var logs = DataService.GetRecentLogsByUsername(user.Username);
			ViewData["name"] = user.FullName;
			return PartialView(logs);
		}

		private CreateLogViewModel NewCreateLogModel()
		{
			MileageLogType logType;
			Enum.TryParse(GetCookieValue(CookieKeyLogtype), true, out logType);
			var vehicleId = GetCookieValue(CookeNameVehicleid);

			return new CreateLogViewModel
			{
				LogType = logType,
				VehicleId = vehicleId,
				Date = DateTime.Today,
				Purpose =
					new SelectListViewModel
					{
						Available =
							GetPurposeTypesSelectList()
					}
			};
		}

		#region Import

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

		[HttpPost]
		public ActionResult Import(ImportUploadViewModel viewModel)
		{
			if (ModelState.IsValid && viewModel.File != null && viewModel.File.ContentLength > 0)
			{
				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)
		{
			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.FindUserByUsername(User.Identity.Name);
				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,
						"Edit", "Default", "EditPast", "CreateLog",
						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,
					"Edit", "Default", "EditPast", "CreateLog",
					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.GetCreateLogViewModel();
			}
			catch
			{
				return string.Empty;
			}

			return RenderRazorViewToString("ImportFix", viewModel);
		}

		[HttpPost]
		public ActionResult ImportFix(ImportLogViewModel viewModel)
		{
			var createGetLogViewModel = viewModel.GetCreateLogViewModel();
			createGetLogViewModel.Purpose = new SelectListViewModel
				{
					Available = GetPurposeTypesSelectList()
				};
			return View("Index", createGetLogViewModel);
		}

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

		#endregion
	}
}