package ru.yandex.wmconsole.service;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.springframework.jdbc.core.simple.ParameterizedRowMapper;

import ru.yandex.common.framework.pager.Pager;
import ru.yandex.common.util.db.LongRowMapper;
import ru.yandex.wmconsole.data.NotificationTypeEnum;
import ru.yandex.wmconsole.data.WMCHistoryObjectTypeEnum;
import ru.yandex.wmconsole.data.info.DraftMessageInfo;
import ru.yandex.wmconsole.data.info.PersonalDraftMessageInfo;
import ru.yandex.wmconsole.data.partition.WMCPartition;
import ru.yandex.wmconsole.service.error.WMCUserProblem;
import ru.yandex.wmtools.common.data.HistoryActionEnum;
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.util.SqlUtil;

/**
 * @author avhaliullin
 */
public class PersonalDraftMessagesService extends AbstractDraftMessagesService<PersonalDraftMessageInfo> {
    private static final String FIELD_RECIPIENTS_COUNT = "rec_count";
    private static final String FIELD_FIRST_RECIPIENT = "rec_first";
    private static final String FIELD_USER_ID = "user_id";

    private static final String SELECT_MESSAGE_RECIPIENTS_LIST_QUERY =
            "SELECT " +
                    "    user_id AS " + FIELD_USER_ID + " " +
                    "FROM " +
                    "    tbl_message_recipients " +
                    "WHERE " +
                    "    message_id = ? " +
                    "ORDER BY " +
                    "    user_id " +
                    " %1$s ";

    private static final String SELECT_MESSAGE_RECIPIENTS_LIST_COUNT_QUERY =
            "SELECT " +
                    "    count(*) " +
                    "FROM " +
                    "    tbl_message_recipients " +
                    "WHERE " +
                    "    message_id = ? ";

    private static final String DELETE_MESSAGE_RECIPIENTS_TEMPLATE_QUERY =
            "DELETE FROM " +
                    "    tbl_message_recipients " +
                    "WHERE " +
                    "    message_id = ? " +
                    "AND " +
                    "    user_id IN (%1$s)";

    private static final String INSERT_MESSAGE_RECIPIENTS_TEMPLATE_QUERY =
            "INSERT IGNORE INTO " +
                    "   tbl_message_recipients (message_id, user_id) " +
                    "VALUES %1$s";


    private static final String ADDITIONAL_FIELDS_SUBQUERY = ", " +
            "   (SELECT " +
            "        count(*) " +
            "    FROM " +
            "        tbl_message_recipients mr " +
            "    WHERE " +
            "        mr.message_id = dm.message_id " +
            "    ) AS " + FIELD_RECIPIENTS_COUNT + ", " +
            "    null AS " + FIELD_FIRST_RECIPIENT + " ";


    private final ParameterizedRowMapper<PersonalDraftMessageInfo> messageInfoMapper = new ParameterizedRowMapper<PersonalDraftMessageInfo>() {
        @Override
        public PersonalDraftMessageInfo mapRow(ResultSet rs, int i) throws SQLException {
            DraftMessageInfo draftMessageInfo = draftMessageInfoMapper.mapRow(rs, i);
            long recCount = rs.getLong(FIELD_RECIPIENTS_COUNT);
            WMUserInfo recFirst = null;
            try {
                long recFirstId = rs.getLong(FIELD_FIRST_RECIPIENT);
                if (!rs.wasNull()) {
                    recFirst = userInfoService.getUserInfo(recFirstId);
                }
            } catch (UserException e) {
                // If an attempt to find user info caused an exception - we leave null value.
                // This is not an outstanding situation.
            } catch (InternalException e) {
                // If an attempt to find user info caused an exception - we leave null value.
                // This is not an outstanding situation.
            }
            return new PersonalDraftMessageInfo(draftMessageInfo, recCount, recFirst);
        }
    };

    @Override
    protected NotificationTypeEnum getNotificationType() {
        return NotificationTypeEnum.PERSONAL_MESSAGE;
    }

    @Override
    protected WMCHistoryObjectTypeEnum getWmcHistoryObjectType() {
        return WMCHistoryObjectTypeEnum.PERSONAL_MESSAGE;
    }

    @Override
    protected ParameterizedRowMapper<PersonalDraftMessageInfo> getMapper() {
        return messageInfoMapper;
    }

    @Override
    protected String getAdditionalFieldsString() {
        return ADDITIONAL_FIELDS_SUBQUERY;
    }

    @Override
    protected Map<String, Object> getNotifierRequestParams(DraftMessageInfo message) throws InternalException {
        return Collections.singletonMap("user-id",
                (Object) SqlUtil.getCommaSeparatedList(getRecipientsListForMessage(message.getMessageId(), null)));
    }

    private void internalAddRecipients(Long messageId, Set<Long> userIdsList) throws InternalException {
        StringBuilder insertValues = new StringBuilder();
        boolean first = true;
        for (Long rec : userIdsList) {
            if (!first) {
                insertValues.append(", ");
            }
            first = false;
            insertValues.append("(").append(messageId).append(", ").append(rec).append(")");
        }
        String insertSql = String.format(INSERT_MESSAGE_RECIPIENTS_TEMPLATE_QUERY, insertValues.toString());
        getJdbcTemplate(WMCPartition.nullPartition()).update(insertSql);
    }

    public void removeRecipients(final long authorId, final long messageId, Long[] recipientIds) throws InternalException, UserException {
        // check permissions
        checkPermissions(authorId);

        // check message type
        PersonalDraftMessageInfo message = getDraftMessage(messageId);
        if (message.getType() != WMCHistoryObjectTypeEnum.PERSONAL_MESSAGE) {
            throw new UserException(WMCUserProblem.WRONG_MESSAGE_TYPE, "Recipients list must be empty for " + WMCHistoryObjectTypeEnum.GLOBAL_MESSAGE);
        }

        // check message is not sent or deleted
        if ((message.getState() != HistoryActionEnum.ADD) && (message.getState() != HistoryActionEnum.EDIT)) {
            throw new UserException(WMCUserProblem.MESSAGE_IS_READ_ONLY, "Now you can't edit any options of this message. It's because of a state: " + message.getState());
        }

        String updateSql = String.format(DELETE_MESSAGE_RECIPIENTS_TEMPLATE_QUERY, SqlUtil.getCommaSeparatedList(Arrays.asList(recipientIds)));
        getJdbcTemplate(WMCPartition.nullPartition()).update(updateSql, messageId);
    }

    public void addRecipients(final long authorId, final long messageId, String recipients, Set<String> incorrectRecipientsList) throws InternalException, UserException {
        if (recipients == null) {
            recipients = "";
        }

        // check permissions
        checkPermissions(authorId);

        // check message type
        PersonalDraftMessageInfo message = getDraftMessage(messageId);
        if (message.getType() != WMCHistoryObjectTypeEnum.PERSONAL_MESSAGE) {
            if (!recipients.equals("")) {
                throw new UserException(WMCUserProblem.WRONG_MESSAGE_TYPE, "Recipients list must be empty for " + message.getType() + " but was: \"" + recipients + "\"");
            }
        }

        // check message is not sent or deleted
        if ((message.getState() != HistoryActionEnum.ADD) && (message.getState() != HistoryActionEnum.EDIT)) {
            throw new UserException(WMCUserProblem.MESSAGE_IS_READ_ONLY, "Now you can't edit any options of this message. It's because of a state: " + message.getState());
        }

        // parse recipients
        final Set<Long> correctRecipientsList = new TreeSet<Long>();
        userInfoService.parseUserList(recipients, correctRecipientsList, incorrectRecipientsList);

        // save recipients list
        if (correctRecipientsList.size() > 0) {
            internalAddRecipients(messageId, correctRecipientsList);
        }
    }

    public List<Long> getRecipientsListForMessage(long messageId, Pager pager) throws InternalException {
        return getJdbcTemplate(WMCPartition.nullPartition()).pageableSelect(SELECT_MESSAGE_RECIPIENTS_LIST_COUNT_QUERY,
                SELECT_MESSAGE_RECIPIENTS_LIST_QUERY, new LongRowMapper(), pager, messageId);
    }

    public long addNewMessage(final long authorId, final String recipients, Set<String> incorrectRecipientsList) throws UserException, InternalException {
        long id = addNewMessage(authorId);
        addRecipients(authorId, id, recipients, incorrectRecipientsList);
        return id;
    }
}
