/*******************************************************************************
 *  Copyright (c) 2015 XLAB d.o.o.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  @author XLAB d.o.o.
 *******************************************************************************/
package eu.cloudscale.showcase.db.services;

import eu.cloudscale.showcase.db.BuyConfirmResult;
import eu.cloudscale.showcase.db.dao.*;
import eu.cloudscale.showcase.db.model.*;
import eu.cloudscale.showcase.db.model.hibernate.ShoppingCart;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Transactional(readOnly = true)
public abstract class AService implements IService {
    @Autowired
    protected IAddressDao addressDao;

    @Autowired
    protected IAuthorDao authorDao;

    @Autowired
    protected ICcXactsDao ccXactsDao;

    @Autowired
    protected ICountryDao countryDao;

    @Autowired
    protected ICustomerDao customerDao;

    @Autowired
    protected IItemDao itemDao;

    @Autowired
    protected IOrderLineDao orderLineDao;

    @Autowired
    protected IOrdersDao ordersDao;

    @Autowired
    protected IShoppingCartDao shoppingCartDao;

    @Autowired
    protected IShoppingCartLineDao shoppingCartLineDao;

    @Override
    @Transactional(readOnly = false)
    public IShoppingCart createEmptyCart() {
        IShoppingCartDao scDao = shoppingCartDao;
        IShoppingCart sc = new ShoppingCart();
        sc.setScTime(new Date());
        //getCurrentSession().saveOrUpdate(sc);
        scDao.shrani(sc);

        return sc;
    }

    @SuppressWarnings("rawtypes")
    @Override
//	@Cacheable("newProducts")
//TODO Jhonny removed cache
    public List getNewProducts(String category) {
        //System.out.println("getNewProducts(" + category + ") called!");
        return itemDao.getNewProducts(category);
    }

    @Override
    @Transactional(readOnly = false)
    public IShoppingCart doCart(IShoppingCart sc, Integer itemId,
                                List<Integer> ids, List<Integer> quantities) {

        IShoppingCartLineDao sclDao = shoppingCartLineDao;
        IShoppingCartDao scDao = shoppingCartDao;

        if (itemId != null) {
            addItem(sc, itemDao.findById(itemId.intValue()));
        }

        refreshCart(sc, ids, quantities);
        addRandomItemToCartIfNecessary(sc);
        resetCartTime(sc);

        //System.out.println("[scDao.shrani] doCart()");

        scDao.shrani(sc, true);

        return sc;
    }

    public void resetCartTime(IShoppingCart sc) {
        try {
            sc.setScTime(new Date());
        } catch (Exception e) {
            System.out.println("ShoppingId = " + sc.getScId());
            e.printStackTrace();
        }
    }

    //@Transactional(readOnly=false)
    public void addRandomItemToCartIfNecessary(IShoppingCart sc) {
        // check and see if the cart is empty. If it's not, we do
        // nothing.
        try {
            // Check to see if the cart is empty
            if (sc.getShoppingCartLines().size() == 0) {
                // Cart is empty
                addItem(sc, itemDao.getRandomItem());
            }

        } catch (java.lang.Exception ex) {
            ex.printStackTrace();
        }
    }

    protected IShoppingCart getCart(Integer shoppingId, double discount) {
        IShoppingCartDao scDao = shoppingCartDao;
        IShoppingCart sc = scDao.findById(shoppingId);
        return sc;
    }

    // protected void addRandomItemToCartIfNecessary(
    // IShoppingCartLineDao sclDao, Integer shoppingId)
    // {
    // IItemDao itemDao = getItemDaoImpl();
    //
    // if ( sclDao.isCartEmpty( shoppingId ) )
    // {
    // IItem randItem = itemDao.getRandomItem();
    // // related_item = getRelated1(sclDao, shoppingId, randItem);
    // addItem( shoppingId, randItem );
    // }
    //
    // }

    // protected IItem getRelated1(IShoppingCartLineDao<IShoppingCartLine>
    // sclDao,
    // Integer shoppingId,
    // IItem randItem)
    // {
    // IItemDao<IItem> itemDao = getItemDaoImpl();
    // IItem relatedItem = itemDao.getRelated1( randItem );
    // return relatedItem;
    // }

    //@Transactional(readOnly=false)
    public void refreshCart(IShoppingCart sc, List<Integer> ids,
                            List<Integer> quantities) {
        IShoppingCartLineDao sclDao = shoppingCartLineDao;
        for (int i = 0; i < ids.size(); i++) {
            int itemId = ids.get(i);
            int QTY = quantities.get(i);
            IShoppingCartLine scl = sclDao.getBySCandItem(sc.getScId(), itemId);
            if (QTY == 0) {
                //System.out.println("[sclDao.delete] addItem()");
                sclDao.delete(scl);
            } else {
                scl.setSclQty(QTY);
                //System.out.println("[sclDao.shrani] refreshCart()");
                sclDao.shrani(scl);
            }
        }

    }

    //@Transactional(readOnly=false)
    public void addItem(IShoppingCart sc, IItem item) {
        IShoppingCartLineDao sclDao = shoppingCartLineDao;
        IShoppingCartDao scDao = shoppingCartDao;

        IShoppingCartLine scl = sclDao.getBySCandItem(sc.getScId(),
                item.getIId());

        if (scl != null) {
            scl.setSclQty(scl.getSclQty() + 1);
            //System.out.println("[sclDao.shrani] addItem()");
            sclDao.shrani(scl);
        } else {
            scl = sclDao.getObject();
            scl.setItem(item);
            scl.setSclQty(1);

            scl.setShoppingCart(sc);
            //System.out.println("[sclDao.shrani] addItem()");
            sclDao.shrani(scl);

            sc.getShoppingCartLines().add(scl);
            //scDao.shrani( sc );
        }

    }

    @Override
    @Transactional(readOnly = false)
    public BuyConfirmResult doBuyConfirm(Integer shoppingId,
                                         Integer customerId, String ccType, long ccNumber, String ccName,
                                         Date ccExpiry, String shipping, String street1, String street2,
                                         String city, String state, String zip, String country) {

        IShoppingCart sc = shoppingCartDao.findById(shoppingId);
        if (sc != null) {
            ICustomer customer = customerDao.findById(customerId);

            double cDiscount = getCDiscount(customer);
            //		IShoppingCart cart = getCart( shoppingId, cDiscount );
            IAddress address = saveAddress(street1, street2, city, state, zip,
                    country);

            IOrders order = saveOrder(address, customer, shipping, sc, cDiscount);

            saveCcXacts(order, ccType, ccNumber, ccName, ccExpiry, sc, address);

            clearCart(shoppingId);
            return new BuyConfirmResult(order, sc);

        }
        return new BuyConfirmResult(null, null);
    }

    @Override
    @Transactional(readOnly = false)
    public BuyConfirmResult doBuyConfirm(Integer shoppingId,
                                         Integer customerId, String ccType, Long ccNumber, String ccName,
                                         Date ccExpiry, String shipping) {

        ICustomer customer = customerDao.findById(customerId);


        double discount = getCDiscount(customer);
        IShoppingCart sc = getCart(shoppingId, discount);
        if (sc != null) {
            IAddress address = customer.getAddress();

            IOrders order = saveOrder(address, customer, shipping, sc, discount);

            saveCcXacts(order, ccType, ccNumber, ccName, ccExpiry, sc, address);

            clearCart(shoppingId);
            return new BuyConfirmResult(order, sc);
        }
        return new BuyConfirmResult(null, null);
    }

    @Transactional(readOnly = false)
    protected void clearCart(Integer shoppingId) {
        IShoppingCartDao scDao = shoppingCartDao;
        IShoppingCart sc = (IShoppingCart) scDao.findById(shoppingId);

        IShoppingCartLineDao sclDao = shoppingCartLineDao;

        Set<IShoppingCartLine> res = sc.getShoppingCartLines();

        if (res != null && !res.isEmpty()) {
            for (IShoppingCartLine scl : res) {
                sclDao.delete(scl);
            }
        }
    }

    @Transactional(readOnly = false)
    protected ICcXacts saveCcXacts(IOrders order, String ccType, long ccNumber,
                                   String ccName, Date ccExpiry, IShoppingCart cart, IAddress address) {
        if (ccType.length() > 10)
            ccType = ccType.substring(0, 10);

        if (ccName.length() > 30)
            ccName = ccName.substring(0, 30);

        ICcXacts ccXacts = ccXactsDao.getObject();
        ccXacts.setCountry(address.getCountry());
        ccXacts.setOrders(order);
        ccXacts.setCxType(ccType);
        ccXacts.setCxNum((int) ccNumber);
        ccXacts.setCxName(ccName);
        ccXacts.setCxExpiry(ccExpiry);
        ccXacts.setCxXactAmt(calculateTotal(cart));
        ccXacts.setCxXactDate(new Date());
        ccXacts.setCountry(address.getCountry());

        ccXactsDao.shrani(ccXacts);

        return ccXacts;
    }

    @Transactional(readOnly = false)
    protected IAddress saveAddress(String street1, String street2, String city,
                                   String state, String zip, String country) {

        IAddress address = addressDao.getObject();

        address.setAddrStreet1(street1);
        address.setAddrStreet2(street2);
        address.setAddrCity(city);
        address.setAddrState(state);
        address.setAddrZip(zip);

        address.setCountry(countryDao.getByName(country));
        addressDao.shrani(address);

        return address;
    }

    @Transactional(readOnly = false)
    protected IOrders saveOrder(IAddress address, ICustomer customer,
                                String shipping, IShoppingCart sc, double discount) {

        Calendar cal = Calendar.getInstance();
        Random rand = new Random();

        IOrders order = ordersDao.getObject();
        order.setCustomer(customer);
        order.setOTax(8.25);
        order.setODate(new Date());
        order.setAddressByOBillAddrId(customer.getAddress());
        order.setAddressByOShipAddrId(address);

        cal.add(Calendar.DATE, rand.nextInt(7));
        order.setOShipDate(cal.getTime());
        order.setOShipType(shipping);
        order.setOStatus("PENDING");
        order.setOTotal(calculateTotal(sc));

        order.setOSubTotal(calculateTotal(sc));
        ordersDao.shrani(order);

        Set<IShoppingCartLine> res = sc.getShoppingCartLines();

        if (res != null && !res.isEmpty()) {
            for (IShoppingCartLine scl : res) {
                IOrderLine ol = orderLineDao.getObject();
                ol.setItem(scl.getItem());
                ol.setOlComment(getRandomString(20, 100));
                ol.setOlDiscount(discount);
                ol.setOlQty(scl.getSclQty());
                ol.setOrders(order);
                order.getOrderLines().add(ol);
                orderLineDao.shrani(ol);
            }
        }

        ordersDao.shrani(order);

        return order;
    }

    public String getRandomString(int min, int max) {
        String newstring = new String();
        Random rand = new Random();
        int i;
        final char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
                'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', '#', '$', '%', '^',
                '&', '*', '(', ')', '_', '-', '=', '+', '{', '}', '[', ']',
                '|', ':', ';', ',', '.', '?', '/', '~', ' '}; // 79
        // characters
        int strlen = (int) Math.floor(rand.nextDouble() * (max - min + 1));
        strlen += min;
        for (i = 0; i < strlen; i++) {
            char c = chars[(int) Math.floor(rand.nextDouble() * 79)];
            newstring = newstring.concat(String.valueOf(c));
        }
        return newstring;
    }

    protected double calculateTotal(IShoppingCart sc) {
        double total = 0;
        if (sc != null) {

            Set<IShoppingCartLine> res = sc.getShoppingCartLines();

            if (res != null && !res.isEmpty()) {
                for (IShoppingCartLine scl : res) {
                    if (scl != null) {
                        total += scl.getItem().getICost();
                    }
                }
            }
        }

        return total;
    }

    protected double getCDiscount(ICustomer customer) {
        return customer.getCDiscount();
    }

    @Override
    public List searchByAuthor(String keyword) {

        List<IAuthor> authors = authorDao.findBySoundexLname(keyword);

        List<IItem> items = new ArrayList<IItem>();

        for (IAuthor author : authors) {
            if (items.size() >= 50) {
                break;
            }

            items.addAll(itemDao.findAllByAuthor(author));
        }

        return items;
    }

    @Override
    public List<IItem> getPromotional() {
        return itemDao.getPromotional();
    }

    @Override
    public boolean countryExist(String country) {
        ICountry country1 = countryDao.getByName(country);
        return country1 == null;
    }

    @Override
    public ICustomer getUserBy(String uname, String passwd) {
        return customerDao.getUserBy(uname, passwd);
    }

    @Override
    public ICustomer getCustomerObject() {
        return customerDao.getObject();
    }

    @Override
    public IAddress getAddressObject() {
        return addressDao.getObject();
    }

    @Override
    public ICountry getCountryByName(String country) {
        return countryDao.getByName(country);
    }

    @Override
    @Transactional(readOnly = false)
    public void saveAddress(IAddress address) {
        addressDao.shrani(address);
    }

    @Override
    @Transactional(readOnly = false)
    public void saveCustomer(ICustomer customer) {
        customerDao.shrani(customer);
    }

    @Override
    public IShoppingCart findShoppingCartById(Integer shoppingId) {
        return shoppingCartDao.findById(shoppingId);
    }

    @Override
    public ICustomer findCustomerById(Integer customerId) {
        return customerDao.findById(customerId);
    }

    @Override
//    @Cacheable("bestSellers")
    public List<Object[]> getBestSellers(String category) {
        return itemDao.getBestSellers(category);
    }

    @Override
//    @Cacheable("search")
    public List<IItem> searchByTitle(String keyword) {
        List<IItem> items = itemDao.findAllByTitle(keyword);

        return items;
    }

    @Override
//    @Cacheable("search")
    public List<IItem> searchBySubject(String keyword) {
        List<IItem> items = itemDao.findAllBySubject(keyword);

        return items;
    }

    @Override
    public IOrders getMostRecentOrder(ICustomer customer) {
        return ordersDao.getMostRecentOrder(customer);
    }

    @Override
    public List<IOrderLine> findAllOrderLineByOrder(IOrders order) {
        return orderLineDao.findAllByOrder(order);
    }

    @Override
    public IItem findItemById(Integer itemId) {
        return itemDao.findById(itemId);
    }

    @Override
    public IAddress findAddressById(Integer addrId) {
        return addressDao.findById(addrId);
    }

    @Override
    public ICountry getCountryById(Integer coId) {
        return countryDao.findById(coId);
    }

    @Override
    public List<ICustomer> findCustomerByAddress(IAddress address) {
        return customerDao.findByAddress(address);
    }

    @Override
    public IOrders findOrdersById(Integer cxOId) {
        return ordersDao.findById(cxOId);
    }

    @Override
    public IAuthor findAuthorById(Integer cxAuthId) {
        try {
            return authorDao.findById(cxAuthId);
        } catch (Exception e) {
            System.out.println("AuthorDao is null");
            return null;
        }
    }

    @Override
    public IOrderLine findOrderLineById(Integer olId) {
        return orderLineDao.findById(olId);
    }

    @Override
    public ICcXacts findCcXactsById(Integer ccXactId) {
        return ccXactsDao.findById(ccXactId);
    }

    @Override
    public List findAllShoppingCartLinesBySC(IShoppingCart shoppingCart) {
        try {
            return shoppingCartDao.findAllBySC(shoppingCart);
        } catch (NullPointerException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    @Transactional(readOnly = false)
    public void saveItem(IItem item) {
        itemDao.shrani(item);
    }
}
