package ru.yandex.wmconsole.periodic;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.RowCallbackHandler;

import ru.yandex.common.scheduler.ExecutionContext;
import ru.yandex.wmconsole.data.partition.WMCPartition;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.util.scheduler.timetable.AbstractTaskExecutor;

/**
 * @author Andrey Mima (amima@yandex-team.ru)
 */
public class RegularUsersCountTask extends AbstractTaskExecutor {
    private static final Logger log = LoggerFactory.getLogger(RegularUsersCountTask.class);

    private static final String FIELD_USER_ID = "user_id";

    private static final String INSERT_REGULAR_USERS_HISTORY_QUERY =
            "INSERT INTO " +
                    "tbl_true_users_history (day, true_users_count) " +
                    "VALUES " +
                    "(?, ?)";

    private static final String SELECT_USERS_FOR_WEEK_QUERY =
            "SELECT DISTINCT " +
                    "       user_id AS " + FIELD_USER_ID + " " +
                    "   FROM " +
                    "       tbl_user_requests_count " +
                    "   WHERE " +
                    "       request_date " +
                    "   BETWEEN " +
                    "       (CURDATE() - INTERVAL ? DAY) " +
                    "   AND " +
                    "       (CURDATE() - INTERVAL ? DAY)";

    private static final int WEEKS_COUNT = 4;

    @Override
    public String runWithRELogging(ExecutionContext context) {
        log.debug("RegularUsersCountTask task fired");
        logUserDbConnections();
        String msg = getClass().getName() + " task executed";
        try {
            final Map<Long, Counter> usersMap = new HashMap<Long, Counter>();

            final Counter usersCounter = new Counter(0);
            for (int i = 0; i < WEEKS_COUNT; i++) {
                final int index = i;
                getJdbcTemplate(WMCPartition.nullPartition()).query(SELECT_USERS_FOR_WEEK_QUERY, new RowCallbackHandler() {
                    @Override
                    public void processRow(ResultSet rs) throws SQLException {
                        if (index == 0) {
                            usersMap.put(rs.getLong(FIELD_USER_ID), new Counter(1));
                        } else {
                            Counter stat = usersMap.get(rs.getLong(FIELD_USER_ID));
                            if (stat != null) {
                                stat.increment();
                                if (index == WEEKS_COUNT - 1 && stat.getValue() == WEEKS_COUNT) {
                                    usersCounter.increment();
                                }
                            }
                        }
                    }
                }, (WEEKS_COUNT - index) * 7, (WEEKS_COUNT - index - 1) * 7 + 1);
            }

            Calendar nowC = Calendar.getInstance();
            nowC.add(Calendar.DAY_OF_MONTH, -1);
            nowC.set(Calendar.HOUR_OF_DAY, 0);
            nowC.set(Calendar.MINUTE, 0);
            nowC.set(Calendar.SECOND, 0);
            nowC.set(Calendar.MILLISECOND, 0);
            Date now = new Date(nowC.getTimeInMillis());

            try {
                saveQuantityInHistory(now, usersCounter.getValue());
            } catch (InternalException e) {
                log.error("Regular users quantity db saving failed", e);
            }
        } catch (InternalException e) {
            msg = "RegularUsersCountTask task failed";
            log.error(msg, e);
        }
        logUserDbConnections();
        return msg;
    }

    public void saveQuantityInHistory(Date day, long count) throws InternalException {
        getJdbcTemplate(WMCPartition.nullPartition()).update(INSERT_REGULAR_USERS_HISTORY_QUERY, day, count);
    }

    private class Counter {
        private int value;

        public Counter(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void increment() {
            value++;
        }
    }
}
