/*******************************************************************************
 * Copyright 2Fi Business Solutions Ltd.
 *
 * This code is copyrighted. Under no circumstances should any party, people,
 * or organization should redistribute any portions of this code in any form,
 * either verbatim or through electronic media, to any third parties, unless
 * under explicit written permission by 2Fi Business Solutions Ltd.
 ******************************************************************************/
package com.ffii.tbms.order.service;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffii.core.dao.JdbcDao;
import com.ffii.core.utils.MapUtils;
import com.ffii.core.utils.Params;
import com.ffii.core.utils.SecurityUtils;
import com.ffii.core.utils.web.ServletRequestUtils;
import com.ffii.core.web.AbstractService;
import com.ffii.tbms.log.LogUtils;
import com.ffii.tbms.log.service.AuditLogService;
import com.ffii.tbms.message.Message;
import com.ffii.tbms.message.service.MessageService;
import com.ffii.tbms.order.Order;
import com.ffii.tbms.order.OrderItem;
import com.ffii.tbms.order.dao.OrderItemDao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.ServletRequestBindingException;

/**
 * @author Patrick
 */
@Service
public class OrderItemService extends AbstractService {

	@Autowired
	private JdbcDao jdbcDao;

	@Autowired
	private OrderItemDao orderItemDao;

	@Autowired
	private OrderService orderService;

	@Autowired
	private MessageService messageService;

	@Autowired
	private AuditLogService auditLogService;

	DecimalFormat df = new DecimalFormat("#,##0.00");

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public Integer saveOrUpdate(OrderItem instance) {
		return orderItemDao.saveOrUpdate(instance);
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = true)
	public OrderItem find(Integer id) {
		return orderItemDao.find(id);
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = true)
	public List<Map<String, Object>> search(Map<String, Object> args) {

		StringBuilder sql = new StringBuilder(
			" SELECT "
			+" oi.id, "
			+" oi.orderId, "
			+" oi.name, "
			+" oi.price "
			+" FROM order_item oi "
			+" WHERE oi.deleted = 0 ");

		if (args != null) {
			if (args.containsKey("orderId"))
				sql.append(" AND oi.orderId = :orderId ");
		}
		sql.append(" ORDER BY oi.id ");

		return jdbcDao.queryForList(sql.toString(), args);
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = true)
	public Map<String, Object> queryForMap(Integer id) {
		String sql = "SELECT * FROM order_item oi WHERE oi.id = :id";

		return jdbcDao.queryForMap(sql, MapUtils.toHashMap(Params.ID, id));
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public void compare(OrderItem item, HttpServletRequest request)throws ServletRequestBindingException {
		String priceStr = ServletRequestUtils.getTrimmedStringParameter(request, "price");
		BigDecimal price = new BigDecimal(priceStr);
		if(price.compareTo(item.getPrice())!=0){
			Order order = orderService.find(item.getOrderId());
			Message message = new Message();
			message.setOrderId(item.getOrderId());
			message.setContent("Amount changed: \n("+order.getType()+order.getCode()+") "
						+item.getName()+"\nAmount: "+df.format(item.getPrice())+" -> (New) "+df.format(price));
			messageService.saveOrUpdate(message);
		}
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public void log(String action, int id, OrderItem newRecord, OrderItem oldRecord)throws JsonProcessingException {
		Map<String, Object> data = new HashMap<>();
		Map<String, Object> compare = LogUtils.compare(newRecord, oldRecord);
		data = (Map<String, Object>)compare.get(LogUtils.DIFF);
		log(action, id, data, newRecord);
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public void log(String action, int id, Map<String, Object> data, OrderItem newRecord)throws JsonProcessingException {
		if(data.size()<=0) return;
		ObjectMapper mapper = new ObjectMapper();
		auditLogService.save("order_item",
							id,
							SecurityUtils.getUser().getId(),
							new Date(),
							action,
							mapper.writeValueAsString(data),
							newRecord.getOrderId(),
							"orders");
	}

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public void log(String action, int id, OrderItem newRecord)throws JsonProcessingException {
		Map<String, Object> data = LogUtils.filter(LogUtils.objectToMap(newRecord));
		log(action, id, data, newRecord);
	}

	// For API
	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public void log(String action, int id, int userId, Map<String, Object> data, OrderItem newRecord)throws JsonProcessingException {
		if(data.size()<=0) return;
		ObjectMapper mapper = new ObjectMapper();
		auditLogService.save("order_item",
							id,
							userId,
							new Date(),
							action,
							mapper.writeValueAsString(data),
							newRecord.getOrderId(),
							"orders");
	}

	//For API
	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public void log(String action, int id, int userId, OrderItem newRecord, OrderItem oldRecord)throws JsonProcessingException {
		Map<String, Object> data = new HashMap<>();
		Map<String, Object> compare = LogUtils.compare(newRecord, oldRecord);
		data = (Map<String, Object>)compare.get(LogUtils.DIFF);
		log(action, id, userId, data, newRecord);
	}

	// For API
	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public void log(String action, int id, int userId, OrderItem newRecord)throws JsonProcessingException {
		Map<String, Object> data = LogUtils.filter(LogUtils.objectToMap(newRecord));
		log(action, id, userId, data, newRecord);
	}

}
