package org.mreib.data.nasdaq.hibernate.data;

/**
 * Created by mreib on 06-11-15.
 */

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.mreib.data.nasdaq.hibernate.model.*;
import org.mreib.data.nasdaq.hibernate.service.ShareService;
import org.mreib.data.nasdaq.json.companydata.CompanysDataList;
import org.mreib.data.nasdaq.json.intradaychartdata.IntradayChartData;
import org.mreib.data.nasdaq.json.morningst.StockReportData;

import javax.ejb.AccessTimeout;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.*;
import java.util.concurrent.TimeUnit;

@ApplicationScoped
public class ShareRepository {

    private static final Log log = LogFactory.getLog(ShareRepository.class);

    @Inject
    private EntityManager entityManager;

    @Inject
    private ShareService shareService;

    public Trade findTradeById(Long id) {
        return entityManager.find(Trade.class, id);
    }

    public Company findCompanyById(String isin) {
        return entityManager.find(Company.class, isin);
    }

    @SuppressWarnings("unchecked")
    public List<Trade> findAllTradeOrderedByIsinAndTradetime() {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();
        Criteria cb = session.createCriteria(Trade.class);
        cb.addOrder(Order.asc("isin, tradetime"));
        return (List<Trade>) cb.list();
        // return members;
    }

    /*
            Company c = empty();
        c.isin = "DK0060137594";
        c.instrument = "CSE3202";
        c.setExternalidexchange("EX$$$$XCSE");
        c.name = "BANG & OLUFSEN";
        c.sector = "Technology";
        c.stockstyle = "Small-Growth";
        c.industry = "Consumer Electronics";
        c.fiscalyearends = "May";
        c.employees = 1871L;
        c.sharesnumber = 43197478L;
        c.website = "www.bang-olufsen.com";
        c.setDescription("Bla");
        return c;
     */
    @SuppressWarnings("unchecked")
    public List<Company> findAllCompanyParamCountryEmpLowHighOrderedByIsin(String isinLike, long employeesLow, long employeesHigh) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();
        Criteria cb = session.createCriteria(Company.class);
        List<String> elist = new ArrayList<String>();
        //elist.add("DK0060137594");
        //elist.add("DK0010234467");
        //cb.add(Restrictions.in("isin",elist));
        cb.add(Restrictions.like("isin", isinLike));
        cb.add(Restrictions.eqOrIsNull("note",""));
        cb.add(Restrictions.between("employees", employeesLow, employeesHigh));
        cb.addOrder(Order.asc("isin"));
        return (List<Company>) cb.list();
        // return members;
    }

    @SuppressWarnings("unchecked")
    public List<Company> findAllCompany400DaysOver2AarOver100000(String lande_string, String valutaer_string) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();


        SQLQuery sqlQuery = session.createSQLQuery(
                "SELECT     company.* \n" +
                "FROM       trade \n" +
                "INNER JOIN company \n" +
                "ON         trade.isin = company.isin \n" +
                "WHERE      To_timestamp(trade.tradetime/1000) > Timenow() - interval '2 year' \n" +
                "AND        substring (trade.isin, 1, 2) IN "+lande_string+" \n" +
                "AND        trade.currency IN "+valutaer_string+" \n" +
                "AND        company.note = '' \n" +
                "AND        trade.volume*price >=100000 \n" +
                "GROUP BY   company.isin \n" +
                "HAVING     to_timestamp(max(tradetime)/1000) > timenow() - interval '21 days' \n" +
                "AND        count(*) >=400").addEntity(Company.class);

        log.info(sqlQuery.toString());
        List list = sqlQuery.list();
        log.info("Antal company results: "+list.size());
        return list;
    }

    @SuppressWarnings("unchecked")
    public List<Company> findAllCompanyOrderedByIsin() {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();
        Criteria cb = session.createCriteria(Company.class);
        cb.addOrder(Order.asc("isin"));
        return (List<Company>) cb.list();
        // return members;
    }

    @SuppressWarnings("unchecked")
    public void updateTradePrices() {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        for (Company company : findAllCompanyOrderedByIsin()) {
            Long maxTradetime = findMaxShareTradedPrice(company);
            if(maxTradetime == null) {
              //Hent max
            } else {
              //hent nye
            }
        }
    }

    @SuppressWarnings("unchecked")
    public Long findMaxShareTradedPrice(Company isin) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();

        CriteriaBuilder qb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Number> cq = qb.createQuery(Number.class);
        Root<Trade> root = cq.from(Trade.class);
        cq.select(qb.max(root.get("tradetime")));
        cq.where(qb.equal(root.get("isin"), qb.parameter(Company.class, "myIsin")));
        Long maxTradetime = (Long) entityManager.createQuery(cq).setParameter("myIsin", isin).getSingleResult();

        return maxTradetime;
    }


    @SuppressWarnings("unchecked")
    public List<Trade> findShareTradedPriceListByIsinAndTradeDayNumber(Company isin, long daynumber) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();
        Criteria cb = session.createCriteria(Trade.class);
        cb.add(Restrictions.like("isin", isin));
        cb.add(Restrictions.eq("daynumber", new Long(daynumber)));
        return (List<Trade>) cb.list();
    }


    @SuppressWarnings("unchecked")
    public List<Trade> findOrderedByIsinAndTradeDayNumber(Company isin, long daynumber) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();
        Criteria cb = session.createCriteria(Trade.class);
        cb.add(Restrictions.like("isin", isin));
        cb.add(Restrictions.like("daynumber", new Long(daynumber)));
        cb.addOrder(Order.asc("isin, tradetime"));
        return (List<Trade>) cb.list();
    }

    /**
     *
     * @param isin
     * @param tid_lo from dato i Date.long. Start på dagen: altså 00:00:00
     * @param tid_hi to dato i Date.long. Start på dagen: altså 00:00:00
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<Trade> findTradesByIsinOrderedByTradeDayNumber(Company isin, long tid_lo, long tid_hi) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();
        Criteria cb = session.createCriteria(Trade.class);
        cb.add(Restrictions.like("isin", isin));
        cb.add(Restrictions.between("daynumber", tid_lo, tid_hi));
        cb.addOrder(Order.desc("tradetime"));
        //cb.setMaxResults(maxResult);
        List<Trade> trades = (List<Trade>) cb.list();
        if(trades.isEmpty()) {
            log.debug("No results with this Criteria:"+cb.toString());
            return trades;
        }
        log.debug("DATA. from:"+trades.get(trades.size()-1).getDaynumberDate()+" | to:"+trades.get(0).getDaynumberDate()+" | antal:"+trades.size());
        return trades;
    }

    /**
     *
     * @param isin
     * @param limit
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<Trade> findTradesByIsinOrderedByTradeDayNumberReturnOnlyLimit(Company isin, int limit) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();
        Criteria cb = session.createCriteria(Trade.class);
        cb.add(Restrictions.like("isin", isin));
        cb.addOrder(Order.desc("tradetime"));
        cb.setMaxResults(limit);
        List<Trade> trades = (List<Trade>) cb.list();
        if(trades.isEmpty()) {
            log.debug("No results with this Criteria:"+cb.toString());
            return trades;
        }
        log.debug("DATA. from:"+trades.get(trades.size()-1).getDaynumberDate()+" | to:"+trades.get(0).getDaynumberDate()+" | antal:"+trades.size());
        return trades;
    }

    @SuppressWarnings("unchecked")
    public List<Trade> findTradesByIsinOrderedByTradeDayNumber(Company isin) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        Session session = (Session) entityManager.getDelegate();
        Criteria cb = session.createCriteria(Trade.class);
        cb.add(Restrictions.like("isin", isin));
        cb.addOrder(Order.desc("tradetime"));
        List<Trade> trades = (List<Trade>) cb.list();
        if(trades.isEmpty()) {
            log.debug("No results with this Criteria:"+cb.toString());
            return trades;
        }
        log.debug("DATA. from:"+trades.get(trades.size()-1).getDaynumberDate()+" | to:"+trades.get(0).getDaynumberDate()+" | antal:"+trades.size());
        return trades;
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @AccessTimeout(value = 20, unit = TimeUnit.MINUTES)
    public void updateAllCompanys2LatestTradePricesAfterDayEnd() {

        for (Company company : findAllCompanyOrderedByIsin()) {

/*            if(!company.getIsin().equalsIgnoreCase(Company.testData1().getIsin())) {
                continue;
            }*/
            Thread t1 = new Thread(new Runnable() {
                public void run() {
                    FromTo fromTo = new FromTo(findMaxShareTradedPrice(company));
                    log.debug("Scrape: " + company.getName() + " " + company.getInstrument() + " From: " + fromTo.getFrom().getTime() + " To:" + fromTo.getTo().getTime());
                    scrapeAndPersistLatestShareTradedPriceHistorically(company, fromTo.getFrom().getTimeInMillis(), fromTo.getTo().getTimeInMillis());
                }
            });
            t1.start();
            log.debug("NOW RUNNING company:" + company.getName());

            try {
                Thread.sleep(1000);                 //1000 milliseconds is one second.
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }

        }
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @AccessTimeout(value = 20, unit = TimeUnit.MINUTES)
    public void updateAllCompanys2LatestFinancial() {

        for (Company company : findAllCompanyOrderedByIsin()) {

/*            if(!company.getIsin().equalsIgnoreCase(Company.testData1().getIsin())) {
                continue;
            }*/
            Thread t1 = new Thread(new Runnable() {
                public void run() {
                    log.debug("Scrape Financial: " + company.getName() + " isin:" + company.getIsin());
                    try {
                        scrapeAndPersistLatestFinancial(company);
                    } catch (Exception e) {
                        log.debug("ERROR Scrape Financial: " + company.getName() + " isin:" + company.getIsin()+" e.getMessage from stacktrace:"+e.getMessage());
                        e.printStackTrace();
                    }
                }
            });
            t1.start();
            log.debug("NOW RUNNING company:" + company.getName());

            try {
                Thread.sleep(3000);                 //3000 milliseconds is one second.
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }

        }
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @AccessTimeout(value = 20, unit = TimeUnit.MINUTES)
    public void updateAllCompanys2LatestKeyratio() {

        for (Company company : findAllCompanyOrderedByIsin()) {

/*            if(!company.getIsin().equalsIgnoreCase(Company.testData1().getIsin())) {
                continue;
            }*/
            Thread t1 = new Thread(new Runnable() {
                public void run() {
                    log.debug("Scrape Keyratio: " + company.getName() + " isin:" + company.getIsin());
                    try {
                        scrapeAndPersistLatestKeyratio(company);
                    } catch (Exception e) {
                        log.debug("ERROR Scrape Keyratio: " + company.getName() + " isin:" + company.getIsin()+" e.getMessage from stacktrace:"+e.getMessage());
                        e.printStackTrace();
                    }
                }
            });
            t1.start();
            log.debug("NOW RUNNING company:" + company.getName());

            try {
                Thread.sleep(3000);                 //3000 milliseconds is one second.
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }

        }
    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @AccessTimeout(value = 20, unit = TimeUnit.MINUTES)
    public long scrapeAndPersistLatestShareTradedPriceHistorically(Company company, long fromdate, long todate) {

        IntradayChartData intradayChartData = new IntradayChartData(company, fromdate, todate);

        long start = new Date().getTime();
        log.debug("scrapeAndPersistLatestShareTradedPriceHistorically start");

        String json_data_structure = intradayChartData.execute();
        ArrayList<Trade> json_text_array_price = intradayChartData.parseJsonData2ShareTradedPriceList(company.calculateCurrency(), json_data_structure);

        long end = new Date().getTime();
        log.debug("scrapeAndPersistLatestShareTradedPriceHistorically end. time spend milliseconds:" + (end - start));

        for (Trade trade : json_text_array_price) {
            try {
                shareService.createOrUpdateShareTradedPrice(trade);
            } catch (Exception e) {
                e.printStackTrace();
                return 0L;
            }
        }

        end = new Date().getTime();
        log.debug("after commiting data end. time spend milliseconds:" + (end - start));

        return 1L;

    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @AccessTimeout(value = 20, unit = TimeUnit.MINUTES)
    public long scrapeAndPersistLatestFinancial(Company company) throws Exception {

        StockReportData stockReportData = new StockReportData(company);
        stockReportData.setFinancialRequest();

        HashMap<String, Financial> financialHashMap = stockReportData.parseHtml2Financial(stockReportData.execute());

        long start = new Date().getTime();
        log.debug("scrapeAndPersistLatestFinancial start");

        long end = new Date().getTime();
        log.debug("scrapeAndPersistLatestFinancial end. time spend milliseconds:" + (end - start));

        shareService.createOrUpdateFinancial(financialHashMap);

        end = new Date().getTime();
        log.debug("after commiting data end. time spend milliseconds:" + (end - start));

        return 1L;

    }

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    @AccessTimeout(value = 20, unit = TimeUnit.MINUTES)
    public long scrapeAndPersistLatestKeyratio(Company company) throws Exception {

        StockReportData stockReportData = new StockReportData(company);
        stockReportData.setKeyratioRequest();

        StringBuilder securityTokenRequest_string = new StringBuilder("");
        HashMap<String, Keyratio> KeyratioHashMap = stockReportData.parseHtml2Keyratio(stockReportData.execute(), securityTokenRequest_string);

        //// TODO: 09-04-16 husk at gemme securityTokenRequest_string på customer;
        stockReportData.setSecurityTokenRequest(securityTokenRequest_string);
        stockReportData.parseHtmlCompanyProfile(stockReportData.execute(), company);


        long start = new Date().getTime();
        log.debug("scrapeAndPersistLatestKeyratio start");

        long end = new Date().getTime();
        log.debug("scrapeAndPersistLatestKeyratio end. time spend milliseconds:" + (end - start));

        shareService.createOrUpdateKeyratio(KeyratioHashMap);

        end = new Date().getTime();
        log.debug("after commiting data end. time spend milliseconds:" + (end - start));

        return 1L;

    }

    @SuppressWarnings("unchecked")
    public long scrapeAndPersistShareTradedPriceAlleHandlerIdag(Company company) {
        // using Hibernate Session and Criteria Query via Hibernate Native API
        IntradayChartData intradayChartData = new IntradayChartData(company);

        long start = new Date().getTime();
        log.debug("Execute start");

        String json_data_structure = intradayChartData.execute();
        ArrayList<Trade> tradeFromWeb = intradayChartData.parseJsonData2ShareTradedPriceList(Currency.getInstance("DKK"), json_data_structure);

        //No data returned from website
        if(tradeFromWeb.size()<1) { return 0L; }

        Trade lastTradeFromWeb = tradeFromWeb.get(tradeFromWeb.size()-1);
        List<Trade> DBshareTradedPriceFromWeb = findShareTradedPriceListByIsinAndTradeDayNumber(lastTradeFromWeb.getIsin(), lastTradeFromWeb.getDaynumber());

        long end = new Date().getTime();
        log.debug("Execute end. Time spend milliseconds: " + (end - start));

        for (Trade trade : tradeFromWeb) {
            try {
                shareService.createOrUpdateShareTradedPrice(trade);
            } catch (Exception e) {
                log.error("while persisting Trade"+e.getMessage());
                e.printStackTrace();
            }
        }

        end = new Date().getTime();
        log.debug("After commiting data end. Time spend totalt milliseconds: " + (end - start));

        return 1L;

    }

    public void updateCompanyData() {

        long start = new Date().getTime();
        log.debug("updateCompanyData start");

        CompanysDataList companysDataList = new CompanysDataList();

        companysDataList.setFrontpage2GetCompanysRequest();
        String htmlDataStructure = companysDataList.executePost();
        ArrayList<Company> companies = companysDataList.parseHtml2Company(htmlDataStructure);

        long end = new Date().getTime();
        log.debug("executeCompanysDataList end. time spend milliseconds:" + (end - start));

        for (Company company : companies) {

/*            if(!company.getIsin().equalsIgnoreCase(Company.testData1().getIsin())) {
                continue;
            }*/

            try {
                //<todo>fixme. Extra info about company does not work - the data keep beeing the same
                //addCompanyOverviewData(company);

                //number of shares - PS not historical
                addCompanyAntalAktierData(company);

                shareService.createCompany(company);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        end = new Date().getTime();
        log.debug("automaticTimeoutUpdateCompanyData20_00 after commiting data end. time spend milliseconds:" + (end - start));

    }

    public Company addCompanyAntalAktierData(Company company) throws Exception {
        long start = new Date().getTime();
        log.debug("updateCompanyOverviewData start");

        CompanysDataList companysDataList = new CompanysDataList();

        companysDataList.setFrontpage2GetAntalAktierRequest(company.getInstrument());
        String jsonText = companysDataList.executeGet();
        companysDataList.parseJsonCompanyDetailData2CompanyData(jsonText, company);

        long end = new Date().getTime();
        log.debug("updateCompanyOverviewData end. time spend milliseconds:" + (end - start));

        return company;
    }

}
