using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using InventoryTraker.Web.Attributes;
using InventoryTraker.Web.Core;
using InventoryTraker.Web.Data;
using InventoryTraker.Web.Models;
using InventoryTraker.Web.Utilities;

namespace InventoryTraker.Web.Controllers
{
	public class InventoryController : ControllerBase
	{
		private readonly AppDbContext _context;
		private readonly IMapper _mapper;

		public InventoryController(AppDbContext context, IMapper mapper)
		{
			_context = context;
			_mapper = mapper;
		}

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

		public JsonResult All()
		{
			var viewModels =
				CurrentInventory()
				.ProjectTo<InventoryViewModel>(_mapper.ConfigurationProvider)
				.ToArray();

			return BetterJson(viewModels);
		}

		public JsonResult Find(int id)
		{
			var inventory = _context.Inventories.Find(id);
			var viewModel = _mapper.Map<InventoryViewModel>(inventory);
			return BetterJson(viewModel);
		}

		private IQueryable<Inventory> CurrentInventory()
		{
			return _context
				.Inventories
				.Where(x => x.Quantity > 0)
				.OrderBy(x => x.InventoryType.Name);
		}

		public ActionResult Export()
		{
			var writer = new InventoryReportWriter();

			var viewModels =
				CurrentInventory()
				.ProjectTo<InventoryViewModel>(_mapper.ConfigurationProvider)
				.ToArray();

			var excel = writer.Write(viewModels);

			var filename = $"Inventory{DateTime.Today:yyyyMMdd}.xlsx";

			return
				new FileContentResult(excel, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
				{
					FileDownloadName = filename
				};
		}

		[ActionLog]
		public JsonResult Add(InventoryAddForm form)
		{
			if (!ModelState.IsValid)
				return GetModelStateErrorListJson();

			var inventory = _mapper.Map<Inventory>(form);
			inventory.InventoryType = _context.InventoryTypes.Find(form.InventoryTypeId);

			if (inventory.InventoryType == null)
				return GetErrorListJson("Inventory Type not found");

			_context.Inventories.Add(inventory);
			inventory.Transactions = new List<Transaction>
			{
				new Transaction
				{
					TransactionType = TransactionType.Added,
					AddedQuantity = inventory.Quantity,
					CurrentQuantity = inventory.Quantity,
					Memo = "Arrival",
					Timestamp = DateTime.Now,
					TransactionDate = inventory.AddedDate
				}
			};
			_context.SaveChanges();

			var model = _mapper.Map<InventoryViewModel>(inventory);
			return BetterJson(model);
		}

		[ActionLog]
		public JsonResult Distribute(InventoryDistributeForm form)
		{
			if (!ModelState.IsValid)
				return GetModelStateErrorListJson();

			if (form.InventoryQuantities == null || !form.InventoryQuantities.Any())
				return GetErrorListJson("Must add at least one item");

			var errors = new List<string>();

			foreach (var quantityForm in form.InventoryQuantities)
			{
				var inventory = _context.Inventories.Find(quantityForm.InventoryId);
				
				// check if it's really there
				if (inventory == null)
				{
					errors.Add($"'{quantityForm.InventoryId}' not found");
					continue;
				}

				if (inventory.Quantity < quantityForm.Quantity)
					errors.Add(
						$"'{inventory.InventoryType.Name}' has only {inventory.Quantity} qty, " +
						$"cannot remove {quantityForm.Quantity}");

				inventory.Quantity -= quantityForm.Quantity;

				var mostRecentTransaction = 
					inventory.Transactions.OrderByDescending(t => t.TransactionDate).First();
				if (form.DistributedDate < mostRecentTransaction.TransactionDate)
					errors.Add($"'{inventory.InventoryType.Name}' most recent transaction " +
					           $"is {mostRecentTransaction.TransactionDate.ToShortDateString()}. " +
					           $"Cannot add a transaction on {form.DistributedDate.ToShortDateString()}.");

				inventory.Transactions.Add(new Transaction
				{
					TransactionType = TransactionType.Distributed,
					RemovedQuantity = quantityForm.Quantity,
					CurrentQuantity = inventory.Quantity,
					Destination = form.Destination,
					Memo = form.Memo,
					Timestamp = DateTime.Now,
					TransactionDate = form.DistributedDate
				});
			}

			if (errors.Any())
				return GetErrorListJson(errors.ToArray());

			_context.SaveChanges();

			return BetterJson(CurrentInventory()
				.ProjectTo<InventoryViewModel>(_mapper.ConfigurationProvider)
				.ToArray());
		}

		[ActionLog]
		public JsonResult Remove(InventoryRemoveForm form)
		{
			if (!ModelState.IsValid)
				return GetModelStateErrorListJson();

			var errors = new List<string>();

			var inventory = _context.Inventories.Find(form.InventoryId);
				
			// check if it's really there
			if (inventory == null)
			{
				errors.Add($"'{form.InventoryId}' not found");
			}
			else
			{
				if (inventory.Quantity < form.Quantity)
					errors.Add(
						$"'{inventory.InventoryType.Name}' has only {inventory.Quantity} qty, " +
						$"cannot remove {form.Quantity}");

				inventory.Quantity -= form.Quantity;

				var mostRecentTransaction =
					inventory.Transactions.OrderByDescending(t => t.TransactionDate).First();
				if (form.RemovedDate < mostRecentTransaction.TransactionDate)
					errors.Add($"'{inventory.InventoryType.Name}' most recent transaction " +
					           $"is {mostRecentTransaction.TransactionDate.ToShortDateString()}. " +
					           $"Cannot add a transaction on {form.RemovedDate.ToShortDateString()}.");

				inventory.Transactions.Add(new Transaction
				{
					TransactionType = form.TransactionType,
					RemovedQuantity = form.Quantity,
					CurrentQuantity = inventory.Quantity,
					Memo = form.Memo,
					Timestamp = DateTime.Now,
					TransactionDate = form.RemovedDate
				});
			}

			if (errors.Any())
				return GetErrorListJson(errors.ToArray());

			_context.SaveChanges();

			return BetterJson(_mapper.Map<InventoryViewModel>(inventory));
		}

		[ActionLog]
		public JsonResult Update(InventoryTypeViewModel form)
		{
			// TODO: add update stuff
			return BetterJson(true);
		}
	}
}