package ru.yandex.wmconsole.service;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

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

import ru.yandex.common.util.collections.Pair;
import ru.yandex.wmconsole.data.VerificationStateEnum;
import ru.yandex.wmconsole.data.partition.WMCPartition;
import ru.yandex.wmconsole.verification.VerificationTypeEnum;
import ru.yandex.wmtools.common.error.InternalException;

/**
 * @author ailyin
 * @author senin
 */
public class VerifyViewerService extends VerifyService {
    private static final String FIELD_USER_ID = "user_id";

    private static final String SELECT_IS_VERIFIED_QUERY =
            "SELECT " +
                    "    COUNT(*) " +
                    "FROM " +
                    "    tbl_users_hosts " +
                    "WHERE " +
                    "    state IN (%1$s) " +
                    "AND " +
                    "    host_id = ? " +
                    "AND " +
                    "    user_id = ? ";

    private static final String SELECT_IS_VERIFIED_2_QUERY =
            "SELECT " +
                    "    COUNT(*) " +
                    "FROM " +
                    "    tbl_users_hosts uh " +
                    "INNER JOIN " +
                    "    tbl_hosts h " +
                    "ON " +
                    "    uh.host_id = h.host_id " +
                    "WHERE " +
                    "    uh.state IN (%1$s) " +
                    "AND " +
                    "    uh.user_id = ? " +
                    "AND " +
                    "    h.name = ? ";

    private static final String SELECT_REGISTERED_HOSTS_COUNT_QUERY =
            "SELECT " +
                    "    COUNT(*) " +
                    "FROM " +
                    "    tbl_users_hosts uh " +
                    "INNER JOIN " +
                    "    tbl_hosts h " +
                    "ON " +
                    "    uh.host_id = h.host_id " +
                    "WHERE " +
                    "    h.name = ? " +
                    "AND " +
                    "    uh.state != " + VerificationStateEnum.NEVER_VERIFIED.getValue() + " " +
                    "AND " +
                    "    uh.verification_type != " + VerificationTypeEnum.CHEAT.value();

    private static final String SELECT_USER_IDS_BY_HOSTNAME_QUERY =
            "SELECT " +
                    "    user_id AS " + FIELD_USER_ID + " " +
                    "FROM " +
                    "    tbl_users_hosts uh " +
                    "INNER JOIN " +
                    "    tbl_hosts h " +
                    "ON " +
                    "    uh.host_id = h.host_id " +
                    "WHERE " +
                    "    h.name = ? " +
                    "AND " +
                    "    uh.state in (%1$s) " +
                    "AND " +
                    "    uh.verification_type != " + VerificationTypeEnum.CHEAT.value();

    private static final String SELECT_CHECK_VERIFICATIONS_QUERY =
            "SELECT " +
                    "    uh.user_id AS user_id," +
                    "    h.name AS host_name " +
                    "FROM " +
                    "    tbl_users_hosts uh " +
                    "INNER JOIN " +
                    "    tbl_hosts h " +
                    "ON " +
                    "    uh.host_id = h.host_id " +
                    "WHERE " +
                    "    uh.state IN (%1$s) " +
                    "    AND " +
                    "    %2$s";

    private static ParameterizedRowMapper<Pair<Long, String>> checkVerificationsMapper = new ParameterizedRowMapper<Pair<Long, String>>() {
        @Override
        public Pair<Long, String> mapRow(ResultSet resultSet, int i) throws SQLException {
            return new Pair<Long, String>(resultSet.getLong("user_id"), resultSet.getString("host_name"));
        }
    };

    @Override
    public boolean isHostVerifiedByUser(long hostId, long userId) throws InternalException {
        String query = String.format(SELECT_IS_VERIFIED_QUERY,
                VerificationStateEnum.getCommaSeparatedListForVerifiedStates());

        return getJdbcTemplate(WMCPartition.nullPartition()).queryForObject(query, Boolean.class, hostId, userId);
    }

    public boolean isHostVerifiedByUser(String hostname, long userId) throws InternalException {
        String query = String.format(SELECT_IS_VERIFIED_2_QUERY,
                VerificationStateEnum.getCommaSeparatedListForVerifiedStates());

        return getJdbcTemplate(WMCPartition.nullPartition()).queryForObject(query, Boolean.class, userId, hostname);
    }

    private String getWhereClauseForUsersHosts(Collection<Pair<Long, String>> pairs) {
        StringBuilder sb = new StringBuilder("(");
        String or = " ";
        for (Pair<Long, String> pair : pairs) {
            sb.append(or)
                    .append(" ( user_id = ")
                    .append(pair.getFirst())
                    .append(" AND h.name = '")
                    .append(pair.getSecond())
                    .append("' ) ");
            or = " OR ";
        }
        sb.append(")");
        return sb.toString();
    }

    public Set<Pair<Long, String>> checkVerificationForUsersAndHosts(Collection<Pair<Long, String>> query) throws InternalException {
        Set<Pair<Long, String>> verified;
        if (!query.isEmpty()) {
            verified = new HashSet<Pair<Long, String>>(getJdbcTemplate(WMCPartition.nullPartition()).query(String.format(
                    SELECT_CHECK_VERIFICATIONS_QUERY, VerificationStateEnum.getCommaSeparatedListForVerifiedStates(), getWhereClauseForUsersHosts(query)),
                    checkVerificationsMapper));
        } else {
            return new HashSet<Pair<Long, String>>();
        }

        return verified;
    }

    public boolean isRegistered(String hostname) throws InternalException {
        return (getJdbcTemplate(WMCPartition.nullPartition()).queryForLong(
                SELECT_REGISTERED_HOSTS_COUNT_QUERY, hostname) > 0);
    }

    public List<Long> getUserIdsByHostname(String hostname) throws InternalException {
        String query = String.format(SELECT_USER_IDS_BY_HOSTNAME_QUERY,
                VerificationStateEnum.getCommaSeparatedListForVerifiedStates());

        return getJdbcTemplate(WMCPartition.nullPartition()).query(query, longMapper, hostname);
    }

    //todo remove this bicycle (ru.yandex.common.util.db.RowMappers)
    private static final ParameterizedRowMapper<Long> longMapper = new ParameterizedRowMapper<Long>() {
        @Override
        public Long mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
            return resultSet.getLong(FIELD_USER_ID);
        }
    };
}
