package ru.yandex.wmconsole.service;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.transaction.TransactionStatus;

import ru.yandex.wmconsole.data.info.HomePageOptionsInfo;
import ru.yandex.wmconsole.data.partition.WMCPartition;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.InternalProblem;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.service.AbstractDbService;
import ru.yandex.wmtools.common.util.ServiceTransactionCallbackWithoutResult;

/**
 * Класс для сохранения и считывания настроек главной страницы пользователя в системе Вебмастер.
 *
 * Основная концепция настроек:
 * сохраняются в БД только настройки, отличные от настроек по умолчанию.
 *
 * User: Alexey Zakharov <azakharov@yandex-team.ru>
 * Date: 30.01.12
 */
public class HomePageOptionsService extends AbstractDbService {
    private static final Logger log = LoggerFactory.getLogger(HomePageOptionsService.class);

    private static final String REDIRECT_TO_SITES_FIELD = "redirect_to_sites";
    private static final String PROMO_TAB_FIELD = "promo_tab";
    private static final String SHOW_TIP_FILED = "show_tip";
    private static final String SHOW_METRIKA_ERROR_FIELD = "show_metrika_error";

    private static final String SELECT_HOME_PAGE_OPTIONS_QUERY =
            "SELECT " + REDIRECT_TO_SITES_FIELD +
            ", " + PROMO_TAB_FIELD +
            ", " + SHOW_TIP_FILED +
            ", " + SHOW_METRIKA_ERROR_FIELD +
            " FROM tbl_homepage_options WHERE user_id = ?";

    private static final String REPLACE_HOME_PAGE_OPTIONS_QUERY =
            "REPLACE INTO tbl_homepage_options (user_id, redirect_to_sites, promo_tab, show_tip, show_metrika_error) VALUES (?, ?, ?, ?, ?)";

    private static final String DELETE_HOME_PAGE_OPTIONS_QUERY =
            "DELETE FROM tbl_homepage_options WHERE user_id = ?";

    private static final HomePageOptionsInfo DEFAULT_HOME_PAGE_OPTIONS = new HomePageOptionsInfo(false, null, true, true);


    private static final ParameterizedRowMapper<HomePageOptionsInfo> HOME_PAGE_OPTION_MAPPER =
            new ParameterizedRowMapper<HomePageOptionsInfo>() {
                @Override
                public HomePageOptionsInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
                    boolean redirectToSites = rs.getBoolean(REDIRECT_TO_SITES_FIELD);
                    Integer promoTab = rs.getInt(PROMO_TAB_FIELD);
                    if (rs.wasNull()) {
                        promoTab = null;
                    }
                    boolean showTip = rs.getBoolean(SHOW_TIP_FILED);
                    boolean showMetrikaError = rs.getBoolean(SHOW_METRIKA_ERROR_FIELD);
                    return new HomePageOptionsInfo(redirectToSites, promoTab, showTip, showMetrikaError);
                }
            };

    /**
     * Получить настройки главной страницы для пользователя. Если в БД настройки нет, возвращаются настройки по умолчанию.
     *
     * @param userId идентификатор пользователя
     * @return настройки
     * @throws InternalException
     */
    public HomePageOptionsInfo getHomePageOptionsInfo(long userId) throws InternalException {
        log.debug("getting home page options for " + userId);
        final List<HomePageOptionsInfo> options = getJdbcTemplate(WMCPartition.nullPartition()).query(
                SELECT_HOME_PAGE_OPTIONS_QUERY,
                HOME_PAGE_OPTION_MAPPER,
                userId
        );

        if (options.size() > 1) {
            // Если найдено несколько настроек, то это означает, что
            // user_id не является первичным ключем в tbl_tbl_homepage_options
            log.error("Home page options size is greater than 1: " + options);
            throw new InternalException(InternalProblem.INTERNAL_PROBLEM, "Home page options size is " + options.size());
        } else if (options.size() == 0) {
            // Если настроек не найдено, это может означать, что пользователь только что добавился и для него еще нет
            // настроек в БД или для него должны действовать настройки по умолчанию (в БД не хранятся)
            return DEFAULT_HOME_PAGE_OPTIONS;
        }

        final HomePageOptionsInfo result = options.iterator().next();

        log.debug("options found for " + userId + " redirect-to-sites:" + result.getRedirectToMySites() +
                  " promo-tab:" + result.getPromoTab() + " show-tip:" + result.getShowTip());

        return result;
    }

    /**
     * Сохранить настройку "сделать мои сайты начальной страницей" для пользователя.
     * NOTE: Настройки по умолчанию в БД не хранятся.
     *
     * @param userId идентификатор пользователя
     * @param redirectToSites новое значение настройки redirect-to-sites
     * @throws InternalException
     */
    public void saveHomePageOptionsInfo(final long userId, final boolean redirectToSites,
            final boolean showMetrikaError) throws InternalException, UserException
    {
        log.debug("saveHomePageOptionsInfo uid:" + userId +
                " redirect-to-sites:" + redirectToSites);
        getServiceTransactionTemplate(WMCPartition.nullPartition()).executeInService(new ServiceTransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) throws UserException, InternalException {
                final HomePageOptionsInfo current = getHomePageOptionsInfo(userId);
                final HomePageOptionsInfo future = current
                        .withRedirectToSites(redirectToSites)
                        .withShowMetrikaError(showMetrikaError);
                saveHomePageOptionsInfo(userId, current, future);
            }
        });
    }

    /**
     * Сохранить настройку "установить фиксированный промо-таб" для пользователя userId.
     * NOTE: Обычный пользователь изменять эту настройку не должен.
     * Использовать только через интерфейс администрирования!!!
     * NOTE: Настройки по умолчанию в БД не хранятся.
     *
     * @param userId идентификатор пользователя
     * @param promoTab новое значение настройки promo-tab
     *
     * @throws InternalException
     */
    public void savePromoTab(long userId, Integer promoTab) throws InternalException {
        log.debug("saveHomePageOptionsInfo uid:" + userId + " promo-tab:" + (promoTab != null ? promoTab : "-"));
        final HomePageOptionsInfo current = getHomePageOptionsInfo(userId);
        final HomePageOptionsInfo future = current.withPromoTab(promoTab);

        saveHomePageOptionsInfo(userId, current, future);
    }

    //
    // Служебные методы
    //

    /**
     * Сохранить настройки для пользователя. Настройки по умолчанию в БД не хранятся.
     *
     * @param userId идентификатор пользователя
     * @param current текущие настройки
     * @param future сохраняемые настройки
     * @throws InternalException
     */
    protected void saveHomePageOptionsInfo(long userId, final HomePageOptionsInfo current, final HomePageOptionsInfo future) throws InternalException {
        log.debug("saveHomePageOptionsInfo uid:" + userId
                    + " redirect-to-sites:" + future.getRedirectToMySites()
                    + " promo-tab:" + future.getPromoTab()
                    + " show-metrika-error: " + future.getShowMetrikaError()
        );
        // Если текущие настройки уже содержат такое же значение настроек, то ничего делать не нужно
        if (future.equals(current)) {
            log.debug("Skip home page options saving because options have already been set");
            return;
        }

        // Если новые значения настроек совпадают со значениями по умолчанию, то удаляем настройки
        if (DEFAULT_HOME_PAGE_OPTIONS.equals(future)) {
            log.debug("Deleting home page options because new options are default");
            getJdbcTemplate(WMCPartition.nullPartition()).update(DELETE_HOME_PAGE_OPTIONS_QUERY, userId);
            return;
        }
        getJdbcTemplate(WMCPartition.nullPartition()).update(REPLACE_HOME_PAGE_OPTIONS_QUERY,
                userId, future.getRedirectToMySites(), future.getPromoTab(), future.getShowTip(), future.getShowMetrikaError());
    }


    public void disableTip(final long userId) throws InternalException, UserException {
        log.debug("disableTips uid:" + userId + " show-tips=false");
        getServiceTransactionTemplate(WMCPartition.nullPartition()).executeInService(new ServiceTransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) throws UserException, InternalException {
                final HomePageOptionsInfo current = getHomePageOptionsInfo(userId);
                final HomePageOptionsInfo future = current.withShowTip(false);
                saveHomePageOptionsInfo(userId, current, future);
            }
        });
    }
}
