package ru.yandex.wmconsole.service;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;

import ru.yandex.common.framework.pager.Pager;
import ru.yandex.common.util.db.StringRowMapper;
import ru.yandex.wmconsole.data.DisplayNameModerationStateEnum;
import ru.yandex.wmconsole.data.info.BriefHostInfo;
import ru.yandex.wmconsole.data.info.DisplayNameInfo;
import ru.yandex.wmconsole.data.info.DisplayNameModerationInfo;
import ru.yandex.wmconsole.data.partition.WMCPartition;
import ru.yandex.wmconsole.service.dao.TblDisplayNameModerationDao;
import ru.yandex.wmtools.common.data.info.WMUserInfo;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.service.IUserInfoService;
import ru.yandex.wmtools.common.util.SqlUtil;
import ru.yandex.wmtools.common.util.TimeFilter;

public class DisplayNameModerationService extends HostnameService {
    private static final Logger log = LoggerFactory.getLogger(DisplayNameModerationService.class);

    private static final String FIELD_ID = "id";
    private static final String FIELD_HOST_ID = "host_id";
    private static final String FIELD_HOST_NAME = "host_name";
    private static final String FIELD_DISPLAY_NAME = "display_name";
    private static final String FIELD_MODERATION_STATE_ID = "state_id";
    private static final String FIELD_MODERATOR_ID = "moderator_id";
    private static final String FIELD_PERFORMED_ON = "performed_on";

    private static final String SELECT_CHANGE_HOST_NAME_REQUESTS_QUERY =
            "SELECT " +
                    "    dnm.id AS " + FIELD_ID + ", " +
                    "    h.host_id AS " + FIELD_HOST_ID + ", " +
                    "    h.name AS " + FIELD_HOST_NAME + ", " +
                    "    dnm.display_name AS " + FIELD_DISPLAY_NAME + ", " +
                    "    dnm.performed_on AS " + FIELD_PERFORMED_ON + " " +
                    "FROM " +
                    "    tbl_hosts h " +
                    "JOIN " +
                    "    tbl_display_name_moderation dnm " +
                    "ON    " +
                    "    h.host_id = dnm.host_id " +
                    "WHERE " +
                    "    state_id = " + DisplayNameModerationStateEnum.IN_PROGRESS.getId() + " " +
                    "    %1$s " +   // name
                    "ORDER BY " +
                    "    dnm.performed_on " +
                    "%2$s";         // limit

    private static final String SELECT_DISPLAY_NAME_MODERATION_QUERY =
            "SELECT " +
                    "    dnm.id AS " + FIELD_ID + ", " +
                    "    h.host_id AS " + FIELD_HOST_ID + ", " +
                    "    h.name AS " + FIELD_HOST_NAME + ", " +
                    "    dnm.display_name AS " + FIELD_DISPLAY_NAME + ", " +
                    "    dnm.state_id AS " + FIELD_MODERATION_STATE_ID + ", " +
                    "    dnm.moderator_id AS " + FIELD_MODERATOR_ID + ", " +
                    "    dnm.performed_on AS " + FIELD_PERFORMED_ON + " " +
                    "FROM " +
                    "    tbl_hosts h " +
                    "JOIN " +
                    "    tbl_display_name_moderation dnm " +
                    "ON " +
                    "    h.host_id = dnm.host_id " +
                    "WHERE " +
                    "    1 = 1 " +
                    "    %1$s " +   // state
                    "    %2$s " +   // time filter
                    "    %3$s " +   // moderator
                    "    %4$s " +   // name
                    "ORDER BY " +
                    "    dnm.performed_on DESC " +
                    "%5$s ";        // limit

    private static final String SELECT_DISPLAY_NAME_MODERATION_COUNT_QUERY =
            "SELECT " +
                    "    count(*) " +
                    "FROM " +
                    "    tbl_display_name_moderation " +
                    "WHERE " +
                    "    1 = 1 " +
                    "    %1$s " +   // state
                    "    %2$s " +   // time filter
                    "    %3$s " +   // moderator
                    "    %4$s ";    // name

    private static final String SELECT_DISPLAY_OR_CURRENT_NAME_BY_HOST_QUERY =
            "SELECT " +
                    "   IFNULL(dnm.display_name, h.name) AS " + FIELD_DISPLAY_NAME + " " +
                    "FROM " +
                    "   tbl_hosts h " +
                    "LEFT JOIN " +
                    "   tbl_display_name_moderation dnm " +
                    "ON " +
                    "   h.host_id = dnm.host_id " +
                    "AND " +
                    "   NOT dnm.state_id = " + DisplayNameModerationStateEnum.REFUSED.getId() + " " +
                    "WHERE " +
                    "   h.host_id = ? ";

    private TblDisplayNameModerationDao tblDisplayNameModerationDao;

    private IUserInfoService userInfoService;

    public void changeModerationState(BriefHostInfo briefHostInfo, DisplayNameModerationStateEnum state, long userId)
            throws InternalException {
        tblDisplayNameModerationDao.updateDisplayNameModerationState(briefHostInfo, state, userId);
    }

    public List<DisplayNameInfo> getChangeDisplayNameRequests(Pager pager, String nameTemplate) throws InternalException {
        String selectQuery = String.format(SELECT_CHANGE_HOST_NAME_REQUESTS_QUERY,
                (nameTemplate != null) ? " AND dnm.display_name LIKE '%%" + nameTemplate + "%%'" : " ",
                "%1$s");

        return getJdbcTemplate(WMCPartition.nullPartition()).
                pageableSelect(getRequestsCountQuery(nameTemplate), selectQuery, changeDisplayNameInfoMapper, pager);
    }

    public int getChangeDisplayNameRequestsCount(String nameTemplate) throws InternalException {
        return getJdbcTemplate(WMCPartition.nullPartition()).safeQueryForInt(getRequestsCountQuery(nameTemplate));
    }

    private String getRequestsCountQuery(String nameTemplate) {
        final String SELECT_CHANGE_HOST_NAME_REQUESTS_COUNT_QUERY =
                "SELECT " +
                        "    COUNT(*) " +
                        "FROM " +
                        "    tbl_display_name_moderation " +
                        "WHERE " +
                        "    state_id = " + DisplayNameModerationStateEnum.IN_PROGRESS.getId() + " " +
                        "    %1$s ";    // name
        return String.format(SELECT_CHANGE_HOST_NAME_REQUESTS_COUNT_QUERY,
                (nameTemplate != null) ? " AND display_name LIKE '%" + nameTemplate + "%'" : " ");
    }

    public String getDisplayOrCurrentName(long hostId) throws InternalException {
        return getJdbcTemplate(WMCPartition.nullPartition())
                .queryForObject(SELECT_DISPLAY_OR_CURRENT_NAME_BY_HOST_QUERY, new StringRowMapper(), hostId);
    }

    public Collection<DisplayNameModerationInfo> getDisplayNameModerationHistory(Long moderatorId, Pager pager,
            TimeFilter timeFilter, DisplayNameModerationStateEnum state, String nameTemplate) throws InternalException {
        String countQuery = String.format(
                SELECT_DISPLAY_NAME_MODERATION_COUNT_QUERY,
                (state != null) ? " AND state_id = " + state.getId() + " " :
                        " AND state_id != " + DisplayNameModerationStateEnum.IN_PROGRESS.getId() + " ",
                SqlUtil.getTimeFilterPart(timeFilter, FIELD_PERFORMED_ON),
                (moderatorId != null) ? " AND moderator_id = " + moderatorId : " ",
                (nameTemplate != null) ? " AND display_name LIKE '%" + nameTemplate + "%'" : " "
        );
        String selectQuery = String.format(
                SELECT_DISPLAY_NAME_MODERATION_QUERY,
                (state != null) ? " AND dnm.state_id = " + state.getId() + " " :
                        " AND dnm.state_id != " + DisplayNameModerationStateEnum.IN_PROGRESS.getId() + " ",
                SqlUtil.getTimeFilterPart(timeFilter, FIELD_PERFORMED_ON),
                (moderatorId != null) ? " AND dnm.moderator_id = " + moderatorId : " ",
                (nameTemplate != null) ? " AND dnm.display_name LIKE '%%" + nameTemplate + "%%'" : " ",
                "%1$s"
        );

        return getJdbcTemplate(WMCPartition.nullPartition()).pageableSelect(countQuery, selectQuery,
                displayNameModerationInfoRowMapper, pager);
    }

    private final ParameterizedRowMapper<DisplayNameModerationInfo> displayNameModerationInfoRowMapper =
            new ParameterizedRowMapper<DisplayNameModerationInfo>() {
                @Override
                public DisplayNameModerationInfo mapRow(ResultSet rs, int i) throws SQLException {
                    long id = rs.getLong(FIELD_ID);
                    long hostId = rs.getLong(FIELD_HOST_ID);
                    String hostname = rs.getString(FIELD_HOST_NAME);
                    String displayName = rs.getString(FIELD_DISPLAY_NAME);
                    Date performedOn = rs.getTimestamp(FIELD_PERFORMED_ON);
                    DisplayNameModerationStateEnum state = DisplayNameModerationStateEnum.R.fromValueOrNull(rs.getInt(FIELD_MODERATION_STATE_ID));

                    WMUserInfo moderatorInfo = null;
                    long moderatorId = rs.getLong(FIELD_MODERATOR_ID);
                    try {
                        if (!rs.wasNull()) {
                            moderatorInfo = userInfoService.getUserInfo(moderatorId);
                        }
                    } catch (UserException e) {
                        log.warn("Failed to retrieve user info for user with id=" + moderatorId);
                    } catch (InternalException e) {
                        log.warn("Failed to retrieve user info for user with id=" + moderatorId);
                    }

                    return new DisplayNameModerationInfo(id, hostId, hostname, displayName, performedOn, state, moderatorInfo);
                }
            };

    private static final ParameterizedRowMapper<DisplayNameInfo> changeDisplayNameInfoMapper =
            new ParameterizedRowMapper<DisplayNameInfo>() {
                @Override
                public DisplayNameInfo mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
                    return new DisplayNameInfo(
                            resultSet.getLong(FIELD_ID),
                            resultSet.getLong(FIELD_HOST_ID),
                            resultSet.getString(FIELD_HOST_NAME),
                            resultSet.getString(FIELD_DISPLAY_NAME),
                            resultSet.getTimestamp(FIELD_PERFORMED_ON)
                    );
                }
            };

    @Required
    public void setUserInfoService(IUserInfoService userInfoService) {
        this.userInfoService = userInfoService;
    }

    @Required
    public void setTblDisplayNameModerationDao(TblDisplayNameModerationDao tblDisplayNameModerationDao) {
        this.tblDisplayNameModerationDao = tblDisplayNameModerationDao;
    }
}
