'use strict';
// Сервис для получения и обработки заказов
app.service('ordersService', ['$http', '$q', '$filter', '$interval', 'config',
function ($http, $q, $filter, $interval, config) {
	
	// Типы заказов
	var orderType = parseArray(config.orderType);
	var isOrdinaryType = orderType.indexOf('Ordinary') >= 0;
	var isPickupType = orderType.indexOf('Pickup') >= 0;
	var isCourierType = orderType.indexOf('Courier') >= 0;

	// Столы
	var showTables = parseArray(config.showTables);
	var hideTables = parseArray(config.hideTables);

	// Специальное имя продукта
	var specialProductNames = parseArray(config.specialProductName);
	
	// Схема работы с доставочным заказом
	var isKDSScheme = config.deliveryScheme == 'KDS';
	var isDeliveryScheme = !isKDSScheme;

	// Свойство времени окончания готовки
	//   (применяется только для не доставочных заказов)
	var completeTimeProperty = config.completeStatus >= 5 ? "ProcessingCompleteTime" : "Processing" + config.completeStatus + "BeginTime";

	// Типы статуса "Готовится"
	var inProgressStatusTypes = {
		deliverTime: 'DeliverTime',
		serveTime: 'ServeTime',
		cookingTime: 'CookingTime',
		remainingTime: 'RemainingTime',
		specialText: 'SpecialText'
	};

	// Типы статуса "Готовится"
	var completeStatusTypes = {
		deliverTime: 'DeliverTime',
		serveTime: 'ServeTime',
		cookingTime: 'CookingTime',
		remainingTime: 'RemainingTime',
		specialText: 'SpecialText'
	};
	
	// Статусы доставки
	var deliveryStatus = {
		unconfirmed: 0,
		new: 1,
		waiting: 2,
		onWay: 3,
        delivered: 4,
        closed: 5,
        cancelled: 6
	};
	
	// Статусы блюд базовых заказов
	var orderItemStatus = {
		added: 0,
		printedNotCooking: 1,
		cookingStarted: 2,
		cookingCompleted: 3,
		served: 4
	};

	// Названия типов заказов (для обратной совместимости)
	var orderTypes = {
		courier: 'Доставка курьером',
		pickup: 'Доставка самовывоз'
	};

	// Типы заказов
	var orderServiceTypes = {
		common: 0,
		deliveryByCourier: 1,
		deliveryByClient: 2
	};

	// Получить заказы
	function getOrders() {
		return getKitchenOrders().then(function (kitchenOrders) {
			return getDeliveries().then(function (deliveries) {
				return getCommonOrders(kitchenOrders, deliveries).then(function (orders) {
					return {
						orders: orders.filter(function (order) { return !!order; }),
						kitchenOrders: kitchenOrders,
						deliveries: deliveries
					}
				});
			});
		}).then(function (res) {
			// Обработка заказов из текущей кассовой смены
			var resultOrders = res.orders.reverse().map(function (order) {
				order.kitchenOrder = findSpecialOrder(order.Url, res.kitchenOrders, 'BaseOrder');
				order.delivery = findSpecialOrder(order.Url, res.deliveries, 'Order');
				return order;
			});
			var clientUrls = resultOrders
				.map(function (order) {
					if (order.delivery && (!order.Customers || !order.Customers.length)) {
						return order.delivery.Client;
					}
				})
				.unique()
				.filter(function (clientUrl) {
					return clientUrl;
				});
			var courierUrls = resultOrders
				.map(function (order) {
					if (order.delivery && order.delivery.Courier) {
						return order.delivery.Courier;
					}
				})
				.unique()
				.filter(function (courierUrl) {
					return courierUrl;
				});

			var clientReqs = clientUrls.map(function (clientUrl) {
				return $http.get(clientUrl).then(function (res) {
					return res && res.data;
				});
			});
			var courierReqs = courierUrls.map(function (courierUrl) {
				return $http.get(courierUrl).then(function (res) {
					return res && res.data;
				});
			});
			return $q.all(clientReqs).then(function (clients) {
				return $q.all(courierReqs).then(function (couriers) {
					resultOrders = resultOrders
						.map(function (order) {
							if (order.delivery) {
								if (order.delivery.Client) {
									order.delivery.ClientData = clients.filter(function (client) {
										return client && (client.url === order.delivery.Client || client.Url === order.delivery.Client);
									})[0];
								}
								if (order.delivery.Courier) {
									order.delivery.CourierData = couriers.filter(function (courier) {
										return courier && (courier.url === order.delivery.Courier || courier.Url === order.delivery.Courier);
									})[0];
								}
							}
							return order;
						})
						// Обработка кухонных заказов, которые остались с прошлых кассовых смен
						.concat(res.kitchenOrders.reverse().map(function (kitchenOrder) {
							return {
								Number: kitchenOrder.Number,
								TableNum: kitchenOrder.TableNum,
								OrderType: kitchenOrder.OrderType,
								OrderServiceType: kitchenOrder.OrderServiceType,
								kitchenOrder: kitchenOrder
							};
						}))
						.map(getResultOrder)
						.filter(function (resultOrder) { return resultOrder; })
						.sort(compareOrders);

					// Количество приготовленных заказов
					resultOrders.lengthCompleted = resultOrders.reduce(function (sum, resultOrder) {
						return sum + resultOrder.completed;
					}, 0);
					return resultOrders;
				})
			})
		});
	}

	// Получить сформированный рузультат для вывода на экран
	function getResultOrder(order) {
		if (!order.kitchenOrder && !order.delivery) return;
		if (!checkOrderType(order)) return;
		if (!checkTable(order)) return;

		var latestOrderItem = getLatestOrderItem(order);
		if (!latestOrderItem) return;

		var resultOrder = {
			number: getOrderNumber(order),
			num: order.Number,
			tableNum: order.TableNum,
			guestName: (order.Customers && order.Customers.length && (order.Customers[0].Name || (order.Customers[0].Client && order.Customers[0].Client.Name))) || latestOrderItem.guestName,
			existsSpecialProduct: latestOrderItem.existsSpecialProduct,
			get status() {
				return getStatus(latestOrderItem);
			},
			completed: latestOrderItem.completed,
			sended: false,
			cookingStart: latestOrderItem.start,
			cookingFinish: latestOrderItem.finish,
			cookingTime: latestOrderItem.cookingTime,
			get remainingTime() {
				return latestOrderItem.remainingTime;
			},
			get remainingIsExpired() {
				return latestOrderItem.remainingIsExpired;
			},
			remainingIsExpiredImmediately: latestOrderItem.remainingIsExpiredImmediately
		};
		
		// Доставочный заказ имеет дополнительные поля
		var dlvr = order.delivery;
		if (dlvr) {
			resultOrder.sended = !!dlvr.SendTime;
			resultOrder.guestName = resultOrder.guestName || dlvr.ClientData && dlvr.ClientData.Name;
			resultOrder.guestPhone = dlvr.Phone && getPhoneFormat(dlvr.Phone);
			resultOrder.guestPhoneFull = dlvr.Phone;
			resultOrder.courierName = dlvr.CourierData && dlvr.CourierData.Name;
			resultOrder.deliverTime = dlvr.ExpectedDeliverTime && new Date(dlvr.ExpectedDeliverTime);
			resultOrder.sendTime = dlvr.SendTime && new Date(dlvr.SendTime);

			var onWayTime = Date.now() - new Date(dlvr.SendTime);
			if (onWayTime < 0) {
				onWayTime = 0;
			}
			resultOrder.onWayTime = dlvr.SendTime && getTimeSpan(onWayTime);
		}
		return resultOrder;
	}

	// Получить номер заказа, отформатированный согласно заданной конфигурации
	function getOrderNumber(order) {
		if (config.orderNumberEncryptAlgorithm == 1) {
            var letters = ['E', 'T', 'P', 'A', 'H' ,'K', 'X', 'C', 'B', 'Y'];
            var fullNumber = pad(order.Number.toString().slice(-3), 3, '0');
            var letter = letters[order.Number % letters.length];
            var num1 = (fullNumber[2]*2 + fullNumber[1]*6 + fullNumber[0]*3+1) % 10;
            var num2 = (fullNumber[2]*1 + fullNumber[1]*7 + fullNumber[0]*2+1) % 10;
            return letter + num1 + num2;
		} else if (config.orderNumberEncryptAlgorithm == 2) {
			if (order.delivery && order.delivery.Comment) {
				var matchResult = order.delivery.Comment.match(/(?<=[#])[^.]+(?=[.])/);
				if (matchResult && matchResult.length > 0) {
					return matchResult[0];
				}
			}
		}
		return pad(order.Number, +config.orderNumberLength, config.leadOrderNumberSymbol);
    }

	// Получить базовые заказы
	function getCommonOrders(kitchenOrders, deliveries) {
		var ordersReqs = kitchenOrders.map(function (kitchenOrder) { return kitchenOrder.BaseOrder; })
			.concat(deliveries.map(function (delivery) { return delivery.Order; }))
			.filter(function(orderUrl) { return !!orderUrl; })
			.unique().map(getCommonOrder);
		return $q.all(ordersReqs);
	}

	// Получить базовый заказ
	function getCommonOrder(orderUrl) {
		return $http.get(orderUrl).then(function(res) {
			return res && res.data;
		});
	}

	// Получить кухонные заказы
	function getKitchenOrders() {
		return $http.get(config.webServiceUrl + '/api/kitchenorders').then(function(res) {
			return res.data.filter(function (kitchenOrder) {
				return kitchenOrder.Items.some(function (orderItem) {
				    return filterOrderItem(orderItem) || (orderItem.Modifiers && orderItem.Modifiers.some(filterOrderItem));
				});
			});
		});
	}

	// Получить доставочные заказы
	function getDeliveries() {
		// Если в конфиге не выбрана доставка самовывозом либо доставка курьером,
		//   то грузить список доставок не имеет смысла.
		if (!isPickupType && !isCourierType) {
			return $q.resolve([]);
		}
		return $http.get(config.webServiceUrl + '/api/deliveries').then(function (res) {
			return res.data.filter(filterDelivery);
		});
	}

	// Отфильтровать доставочные заказы
	function filterDelivery(delivery) {
		return deliveryStatus.unconfirmed <= delivery.Status && delivery.Status <= deliveryStatus.onWay &&
			(!config.hideIfDeliveryTimeIsLaterThanMinutes || !delivery.ExpectedDeliverTime ||
				new Date(delivery.ExpectedDeliverTime) < (Date.now() + config.hideIfDeliveryTimeIsLaterThanMinutes * 60000));
	}

	// Поиск кухонного или доставочного заказа по базовому заказу
	function findSpecialOrder(orderUrl, specialOrders, orderProp) {
		var specialOrder = specialOrders.filter(function (specialOrder) {
			return specialOrder[orderProp] == orderUrl;
		})[0];
		var specialOrderPos = specialOrders.indexOf(specialOrder);
		if (specialOrderPos >= 0) {
			specialOrders.splice(specialOrderPos, 1);
		}
		return specialOrder;
	}

	// Фильтрация по типу заказа
	function checkOrderType (order) {
		// Для обратной совместимости
		if (order.OrderServiceType === undefined) {
			return isOrdinaryType && (order.OrderType != orderTypes.pickup && order.OrderType != orderTypes.courier)
				|| isPickupType && order.OrderType == orderTypes.pickup
				|| isCourierType && order.OrderType == orderTypes.courier;
		}

		return (isOrdinaryType && order.OrderServiceType == orderServiceTypes.common
				|| isPickupType && order.OrderServiceType == orderServiceTypes.deliveryByClient
				|| isCourierType && order.OrderServiceType == orderServiceTypes.deliveryByCourier)
			&& (!config.orderTypeNames
				|| !config.orderTypeNames.length
				|| config.orderTypeNames.indexOf(order.OrderType) >= 0);
	}

	// Фильтрация по столу
	function checkTable (order) {
		return (!showTables.length || showTables.some(function (tableNum) {	return tableNum == order.TableNum; }))
			&& (!hideTables.length || !hideTables.some(function (tableNum) { return tableNum == order.TableNum; }));
	}

	// Получение последнего блюда в заказе
	function getLatestOrderItem (order) {
		var orderItems = isKDSScheme || !order.IsDeliveryOrder ?
			(order.kitchenOrder ? order.kitchenOrder.Items : []) :
			order.Guests.reduce(function (allItems, guest) { return allItems.concat(guest.Items); }, []);
		var orderItem = orderItems
            .filter(function(orderItem) {
                return filterOrderItem(orderItem) || (orderItem.Modifiers && orderItem.Modifiers.some(filterOrderItem));
            })
			.map(function (orderItem) {
				return createOrderItem(orderItem, orderItems);
			})
			.sort(compareOrderItems)[0];
		if (orderItem) {
			orderItem.order = order;
			orderItem.guestName = getKitchenOrderGuestName(orderItems);
			orderItem.existsSpecialProduct = existsSpecialProduct(orderItems);
		}
		return orderItem;
	}

	// Получение имени гостя для кухонного заказа
	function getKitchenOrderGuestName(orderItems) {
		var guestNameFromOrderItem;
		if (config.guestNameProductNumber) {
			guestNameFromOrderItem = orderItems
				.filter(function (x) { return x.Product && x.Product.split('/').reverse()[0] == config.guestNameProductNumber; })
				.map(function (x) {	return x.Comment; })[0];
		}
		return guestNameFromOrderItem || config.guestNameDefault || '';
	}

	// Проверить, содержится ли в имени блюд заказа специальное слово из настроек (specialProductName)
	function existsSpecialProduct(orderItems) {
		if (specialProductNames.length) {
			return orderItems
				.some(function (x) { 
					return specialProductNames.some(function(y) { 
						return (x.ProductName && x.ProductName.indexOf(y) >= 0) || (x.Name && x.Name.indexOf(y) >= 0);
					});
				});
		}
		return false;
	}

	// Отфильтровать блюда в заказе по статусам
	//   (применяется только для не доставочных заказов)
	function filterOrderItem(orderItem) {
		return !orderItem.Deleted &&
			(orderItem.ProcessingStatus === undefined ||
			(config.showStatus <= orderItem.ProcessingStatus &&
				orderItem.ProcessingStatus < config.hideStatus &&
				!isCompleteStatusTimeout(orderItem)));
	}

	// Получения признака, что заказ готов либо он пометился как готовый по таймауту
	function isCompleteStatusTimeout(orderItem) {
		if (!isCompleted(orderItem)) {
			return false;
		}
		if (!+config.completeStatusTimeout) {
			return false;
		}
		if (!orderItem[completeTimeProperty]) {
			return false;
		}
		var completeTime = new Date(orderItem[completeTimeProperty]);
		var timeout = +config.completeStatusTimeout * 1000;
		return (+completeTime + timeout) < Date.now();
	}

	// Получения признака, что заказ готов
	function isCompleted(orderItem) {
		return orderItem.ProcessingStatus === undefined ?
			orderItem.Status >= orderItemStatus.cookingCompleted :
			orderItem.ProcessingStatus >= config.completeStatus;
	}
	
	// Конвертация блюд заказа
	function createOrderItem (orderItem, orderItems) {
		var result = {
			deleted: orderItem.Deleted,
			completed: isCompleted(orderItem)
		};
		
		var cookingStartTime = orderItem.EstimatedCookingBeginTime === undefined ?
			orderItem.CookingStartTime : orderItem.EstimatedCookingBeginTime;
		var cookingTimeStr = orderItem.CookingTime;

		// Сценарий одновременной подачи блюд
		if (orderItem.EstimatedCookingBeginTime === null) {
			var latestOrderItemInServeGroup = getLatestOrderItemInServeGroup(orderItems);
			if (latestOrderItemInServeGroup) {
				cookingStartTime = latestOrderItemInServeGroup.EstimatedCookingBeginTime;
				cookingTimeStr = latestOrderItemInServeGroup.CookingTime;
			}
		}

		if (cookingStartTime) {
			var start = new Date(cookingStartTime);
			var cookingTime = 0;
			if (cookingTimeStr) {
				var spl = cookingTimeStr.split(':');
				cookingTime = (+spl[0] * 360 + +spl[1] * 60 + +spl[2]) * 1000;
			}
			var finish = new Date(+start + cookingTime);

			result.start = start;
			result.finish = finish;
			result.cookingTime = getTimeSpan(cookingTime);
			Object.defineProperty(result, 'remainingTime', {
				get: function() {
					var remainingTime = getRemainingTime(orderItem, finish);
					return getTimeSpan(Math.abs(remainingTime));
				}
			});
			Object.defineProperty(result, 'remainingIsExpired', {
				get: function() {
					var remainingTime = getRemainingTime(orderItem, finish);
					return remainingTime < -60000;
				}
			});
			Object.defineProperty(result, 'remainingIsExpiredImmediately', {
				get: function() {
					var remainingTime = getRemainingTime(orderItem, finish);
					return remainingTime < 0;
				}
			});
		}
		return result;
	}

	// Получить блюдо с максимальным предполагаемым временем начала приготовления в сценарии одновременной подачи блюд
	function getLatestOrderItemInServeGroup(orderItems) {
		return orderItems.filter(function (orderItem) {
			return orderItem.ServeGroupNumber === orderItem.ServeGroupNumber && orderItem.EstimatedCookingBeginTime;
		})[0];
	}

	// Получить оставшееся время готовки
	function getRemainingTime(orderItem, finish) {
		var completeTime = orderItem[completeTimeProperty] || orderItem.CookingFinishTime;
		return finish - (completeTime ? new Date(completeTime) : Date.now());
	}

	// Получить временной промежуток
	//   (метод возвращает Date равный 01.01.2000 с
	//   прибавленным к нему временем в миллисекундах)
	function getTimeSpan (milliseconds) {
		var date = new Date(2000, 0);
		date.setTime(+date + milliseconds);
		return date;
	}

	// Сравнить блюда в заказе
	//   (deleted asc, completed asc, finish desc)
	function compareOrderItems(a, b) {
		return a.deleted - b.deleted || a.completed - b.completed || !!b.finish - !!a.finish || b.finish - a.finish;
	}

	// Получить статус заказа
	//   (вычисляется по последнему блюду в заказе)
	function getStatus (orderItem) {
		if (orderItem.completed) {
			// Проверка необходима для обратной совместимости с конфигом, когда в настройке completeStatusName прописывалась просто строка
			if (config.completeStatusName && config.completeStatusName.format && config.completeStatusName.type) {
				var format = config.completeStatusName.format;
				switch(config.completeStatusName.type){
					case completeStatusTypes.remainingTime:
						return getRemainingTimeStatus(orderItem, format);
					case completeStatusTypes.specialText:
						return format;
					default:
						return '';
				}
			}
			return config.completeStatusName;
		} else {
			var format = config.inProgressStatusName.format;
			switch(config.inProgressStatusName.type){
				case inProgressStatusTypes.serveTime:
					return $filter('date')(orderItem.finish, format);
				case inProgressStatusTypes.cookingTime:
					return $filter('date')(orderItem.cookingTime, format);
				case inProgressStatusTypes.remainingTime:
					return getRemainingTimeStatus(orderItem, format);
				case inProgressStatusTypes.specialText:
					return format;
				case inProgressStatusTypes.deliverTime:
					return orderItem.order.delivery && orderItem.order.delivery.ExpectedDeliverTime
						? $filter('date')(new Date(orderItem.order.delivery.ExpectedDeliverTime), format) : '';
				default:
					return '';
			}
		}
	}

	// Получить оставшееся время готовки для отображения
	function getRemainingTimeStatus(orderItem, format) {
		if (orderItem.remainingTime == undefined) {
			return '';
		}
		var waiting = +orderItem.remainingTime;
		// Оставшееся время готовки не должно уходить в минус
		if (orderItem.remainingIsExpiredImmediately || waiting < 0) {
			waiting = 0;
		}
		return $filter('date')(getTimeSpan(waiting), format);
	}

	// Получить номер телефона в формате *N-NN
	function getPhoneFormat (phone) {
		return '*' + phone.slice(-3, -2) + '-' + phone.slice(-2);
	}

	// Сравнить заказы
	//   (completed desc, deliverTime asc, number asc)
	function compareOrders(a, b) {
		return b.completed - a.completed || a.deliverTime - b.deliverTime || a.num - b.num;
	}

	// Обновить список готовых заказов, которые будут
	//   выводиться во всплывающем окне
	function updateSplashOrders (splashOrders, orders) {
		var completedOrders = orders.filter(function (order) {
			return order.completed;
		});
		for (var i = splashOrders.length - 1; i >= 0; i--) {
			var isServed = completedOrders.every(function (order) {
				return order.number != splashOrders[i].number
			});
			if (splashOrders[i].isShown && isServed) {
				splashOrders.splice(i, 1);
			}
		};
		completedOrders.forEach(function (order) {
			var isNewCompletedOrder = splashOrders.every(function (splashOrder) {
				return splashOrder.number != order.number;
			});
			if (isNewCompletedOrder) {
				splashOrders.push(order);
			}
		});
	}

	// Получить заказ для вывода во всплывающем окне
	function getSplashOrder (splashOrders) {
		var order = splashOrders.filter(function (splashOrder) {
			return !splashOrder.isShown;
		})[0];
		if (order) {
			order.isShown = true;
			return order;
		}
	}
	
	// Дополненить строку лидирующими символами
	//   (если длина строки больше требуемой, то исходная строка
	//   обрезается слева до необходимой длины)
	function pad(value, length, symbol) {
		value = value + '';
		var result = value.substr(value.length - length);
		if (value.length < length) {
			result = new Array(length - value.length + 1).join(symbol) + value;
		}
		return result;
	}

	// Преобразовать строку в массив (разделитель - символ ',')
	function parseArray(value) {
		return !value ? [] : value.split(',').map(function (item) {
			return item.trim();
		});
	}

	// Получить только уникальные элементы массива
	Array.prototype.unique = function () {
		var u = {}, a = [];
		for(var i = 0, l = this.length; i < l; ++i){
			if(u.hasOwnProperty(this[i])) {
				continue;
			}
			a.push(this[i]);
			u[this[i]] = 1;
		}
		return a;
	};

	return {
		getOrders: getOrders,
		updateSplashOrders: updateSplashOrders,
		getSplashOrder: getSplashOrder
	};
}]);
