package ru.yandex.wmconsole.service.dao;

import org.jetbrains.annotations.Nullable;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import ru.yandex.common.util.collections.Cu;
import ru.yandex.common.util.db.LongRowMapper;
import ru.yandex.webmaster.common.urltree.YandexSearchShard;
import ru.yandex.wmconsole.data.HostInfoStatusEnum;
import ru.yandex.wmconsole.data.info.HostDbHostInfo;
import ru.yandex.wmconsole.data.info.all.about.url.ShortUrlInfo;
import ru.yandex.wmconsole.data.info.all.about.url.UrlInfoStatusEnum;
import ru.yandex.wmconsole.data.info.all.about.url.UrlRequestId;
import ru.yandex.wmconsole.data.info.all.about.url.UrlResultInfo;
import ru.yandex.wmconsole.data.partition.WMCPartition;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.service.AbstractDbService;
import ru.yandex.wmtools.common.util.SqlUtil;

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

/**
 * User: azakharov
 * Date: 24.10.13
 * Time: 12:29
 */
public class TblAllAboutUrlResultsDao extends AbstractDbService {

    private static final String FIELD_NUM = "num";
    private static final String FIELD_URL = "url";
    private static final String FIELD_NORMALIZED_URL = "normalized_url";
    private static final String FIELD_CREATE_DATE = "create_date";
    private static final String FIELD_IS_ERROR = "is_error";
    private static final String FIELD_SEARCHABLE_XML_TIME = "searchable_xml_time";
    private static final String FIELD_PENALTY_LAST_ACCESS = "penalty_last_access";
    private static final String FIELD_FROM_SUPPORT = "from_support";

    public void updateFastIndexCount(HostDbHostInfo hostDbHostInfo, UrlRequestId requestId, boolean isFromFastRobot) throws InternalException {
        final String query = "UPDATE tbl_all_about_url_results SET is_from_fast_robot = ? WHERE host_id = ? AND num = ?";
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(
                query, isFromFastRobot, hostDbHostInfo.getHostDbHostId(), requestId.getId());
    }

    public void updateLinksCount(HostDbHostInfo hostDbHostInfo, UrlRequestId requestId, long linksCount, long internalLinksCount) throws InternalException {
        final String query = "UPDATE tbl_all_about_url_results SET links_count = ?, internal_links_count = ? " +
                "WHERE host_id = ? AND num = ?";
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(
                query, linksCount, internalLinksCount, hostDbHostInfo.getHostDbHostId(), requestId.getId());
    }

    public void updateSearchableXml(HostDbHostInfo hostDbHostInfo, UrlRequestId requestId, boolean searchableXml) throws InternalException {
        final String query = "UPDATE tbl_all_about_url_results " +
                "SET searchable_xml = ? " +
                "WHERE host_id = ? AND num = ?";
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(
                query, searchableXml, hostDbHostInfo.getHostDbHostId(), requestId.getId());
    }

    public List<ShortUrlInfo> getUrlInfoResultsList(HostDbHostInfo hostDbHostInfo, boolean fromSupport) throws InternalException {
        final String query =
                "SELECT num, url, normalized_url, create_date, is_error, searchable_xml_time, penalty_last_access " +
                        "FROM " +
                        "   tbl_all_about_url_results " +
                        "WHERE " +
                        "   host_id = ? " +
                        "AND " +
                        "   from_support = ? " +
                        "ORDER BY create_date DESC";

        List<ShortUrlInfo> ready = getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).query(
                query, resultsListMapper, hostDbHostInfo.getHostDbHostId(), fromSupport);
        return ready;
    }

    public List<ShortUrlInfo> getUrlInfoResultsList(final HostDbHostInfo hostDbHostInfo,
                                                    final List<Long> ids,
                                                    final boolean fromSupport) throws InternalException {
        final String query =
                "SELECT num, url, normalized_url, create_date, is_error, searchable_xml_time, penalty_last_access " +
                        "FROM " +
                        "   tbl_all_about_url_results " +
                        "WHERE " +
                        "   host_id = ? " +
                        "AND " +
                        "   from_support = ? " +
                        "AND " +
                        "   num IN (" + SqlUtil.getCommaSeparatedList(ids) + ") " +
                        "ORDER BY create_date DESC";

        List<ShortUrlInfo> ready = getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).query(
                query, resultsListMapper, hostDbHostInfo.getHostDbHostId(), fromSupport);
        return ready;
    }

    public List<ShortUrlInfo> getUrlInfoResultsForUrl(HostDbHostInfo hostDbHostInfo, String url, boolean fromSupport) throws InternalException {
        final String SELECT_URL_REPORTS_HISTORY_QUERY =
                "SELECT num, url, normalized_url, create_date, is_error, searchable_xml_time, penalty_last_access " +
                        "FROM " +
                        "   tbl_all_about_url_results " +
                        "WHERE " +
                        "   host_id = ? " +
                        "AND " +
                        "   from_support = ? " +
                        "AND " +
                        "   url = ? " +
                        "ORDER BY create_date DESC";
        List<ShortUrlInfo> ready = getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).query(
                SELECT_URL_REPORTS_HISTORY_QUERY, resultsListMapper, hostDbHostInfo.getHostDbHostId(), fromSupport, url);
        return ready;
    }

    public @Nullable
    ShortUrlInfo getUrlInfoResultSummary(HostDbHostInfo hostDbHostInfo, UrlRequestId requestId, boolean fromSupport) throws InternalException {
        final String query =
                "SELECT num, url, normalized_url, create_date, is_error, searchable_xml_time, penalty_last_access " +
                        "FROM tbl_all_about_url_results " +
                        "WHERE " +
                        "   host_id = ? " +
                        "AND " +
                        "   from_support = ? " +
                        "AND " +
                        "   num = ?";
        return getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).safeQueryForObject(
                query, resultsListMapper, hostDbHostInfo.getHostDbHostId(), fromSupport, requestId.getId());
    }

    public int getUrlInfoResultsCount(HostDbHostInfo hostDbHostInfo, boolean fromSupport) throws InternalException {
        final String query = "SELECT COUNT(*) FROM tbl_all_about_url_results WHERE host_id = ? AND from_support = ? ";
        return getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).queryForInt(
                query, hostDbHostInfo.getHostDbHostId(), fromSupport);
    }

    public List<Long> getOldestUrlInfoResults(HostDbHostInfo hostDbHostInfo, int limit, boolean fromSupport) throws InternalException {
        final String SELECT_OLDEST_REPORTS_QUERY =
                "SELECT " +
                        "   num " +
                        "FROM " +
                        "   tbl_all_about_url_results " +
                        "WHERE " +
                        "   host_id = ? AND from_support = ? " +
                        "ORDER BY create_date ASC LIMIT %d";
        String query = String.format(SELECT_OLDEST_REPORTS_QUERY, limit);
        return getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).query(
                query,
                new LongRowMapper(),
                hostDbHostInfo.getHostDbHostId(),
                fromSupport);
    }

    public void deleteUrlInfoResults(HostDbHostInfo hostDbHostInfo, List<Long> resultIds) throws InternalException {
        final String query = "DELETE FROM tbl_all_about_url_results WHERE host_id = ? AND num IN (%s)";
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(
                String.format(query, SqlUtil.getCommaSeparatedList(resultIds)),
                hostDbHostInfo.getHostDbHostId()
        );
    }

    public void deleteUrlInfoResult(HostDbHostInfo hostDbHostInfo, UrlRequestId resultId, boolean fromSupport) throws InternalException {
        final String query = "DELETE FROM tbl_all_about_url_results WHERE host_id = ? AND num = ? AND from_support = ? ";
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(
                query, hostDbHostInfo.getHostDbHostId(), resultId.getId(), fromSupport);
    }

    public void updateHostInfoStatus(HostDbHostInfo hostDbHostInfo, UrlRequestId resultId, HostInfoStatusEnum status, Date penaltyLastAccess) throws InternalException {
        final String query = "UPDATE tbl_all_about_url_results " +
                "SET host_info_status = ?, penalty_last_access = ? WHERE host_id = ? AND num = ?";
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(
                query, status != null ? status.getValue(): null, penaltyLastAccess,
                hostDbHostInfo.getHostDbHostId(), resultId.getId());
    }

    public List<UrlResultInfo> getReportsWaitingForExtraData(final int dbNumber, final int partitionNumber) throws InternalException {
        final String query =
                "SELECT host_id, num, url, normalized_url, create_date, is_error, links_count, internal_links_count, is_from_fast_robot, searchable_xml, searchable_xml_time, shard_id, penalty_last_access, from_support " +
                        "FROM tbl_all_about_url_results " +
                        "WHERE " +
                        "   (searchable_xml_time IS NULL OR penalty_last_access IS NULL) " +
                        "AND " +
                        "   create_date > DATE_SUB(NOW(), INTERVAL 1 HOUR) " +
                        "ORDER BY create_date ASC " +
                        "LIMIT 1024";

        return getJdbcTemplate(new WMCPartition(dbNumber, partitionNumber)).query(query, urlResultInfoMapper);
    }

    public void updateOldReportsWaitingForExtraData(final int dbNumber, final int partitionNumber) throws InternalException {
        final String query =
                "UPDATE tbl_all_about_url_results SET is_error = 1 " +
                        "WHERE " +
                        "   (searchable_xml_time IS NULL OR penalty_last_access IS NULL) " +
                        "AND " +
                        "   create_date < DATE_SUB(NOW(), INTERVAL 1 HOUR) ";
        getJdbcTemplate(new WMCPartition(dbNumber, partitionNumber)).update(query);
    }

    public void updateXmlSearchDataUpdateTime(final HostDbHostInfo hostDbHostInfo, final UrlRequestId requestId) throws InternalException {
        final String query =
                "UPDATE tbl_all_about_url_results SET searchable_xml_time = NOW() " +
                        "WHERE host_id = ? AND num = ?";
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(query, hostDbHostInfo.getHostDbHostId(), requestId.getId());
    }

    public void updateSearchShard(final HostDbHostInfo hostDbHostInfo, final UrlRequestId requestId, final YandexSearchShard searchShard) throws InternalException {
        final String query =
                "UPDATE tbl_all_about_url_results SET shard_id = ? " +
                        "WHERE host_id = ? AND num = ?";
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(query, searchShard.value(), hostDbHostInfo.getHostDbHostId(), requestId.getId());
    }

    public Map<String, Object> dumpAllAboutUrlResultsInfo(final HostDbHostInfo hostDbHostInfo, final UrlRequestId requestId) throws InternalException {
        final String q =
                "SELECT " +
                "   num, host_id, create_date, url, normalized_url, is_error, host_info_status, penalty_last_access, " +
                "   is_from_fast_robot,  searchable_xml, searchable_xml_time, links_count, internal_links_count, from_support " +
                "FROM tbl_all_about_url_results WHERE host_id = ? AND num = ?";
        List<Map<String, Object>> rs = getJdbcTemplate(new WMCPartition(hostDbHostInfo, null))
                                            .query(q, dumpTableMapper, hostDbHostInfo.getHostDbHostId(), requestId.getId());
        return Cu.emptyIfNull(Cu.firstOrNull(rs));
    }

    private static final ParameterizedRowMapper<Map<String,Object>> dumpTableMapper = new MapMapper();
    public static class MapMapper extends ColumnMapRowMapper implements ParameterizedRowMapper<Map<String, Object>> {
    }

    private static final ParameterizedRowMapper<ShortUrlInfo> resultsListMapper = new ParameterizedRowMapper<ShortUrlInfo>() {
        @Override
        public ShortUrlInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
            return ShortUrlInfo.createUrlInfoFromResults(
                    new UrlRequestId(rs.getLong(FIELD_NUM)),
                    rs.getString(FIELD_URL),
                    SqlUtil.getStringNullable(rs, FIELD_NORMALIZED_URL),
                    rs.getTimestamp(FIELD_CREATE_DATE),
                    rs.getBoolean(FIELD_IS_ERROR),
                    SqlUtil.safeGetTimestamp(rs, FIELD_SEARCHABLE_XML_TIME),
                    SqlUtil.safeGetTimestamp(rs, FIELD_PENALTY_LAST_ACCESS));
        }
    };

    private static final ParameterizedRowMapper<UrlResultInfo> urlResultInfoMapper = new ParameterizedRowMapper<UrlResultInfo>() {
        private static final String FIELD_HOST_ID = "host_id";
        private static final String FIELD_IS_FROM_FAST_ROBOT = "is_from_fast_robot";
        private static final String FIELD_SEARCHABLE_XML = "searchable_xml";
        private static final String FIELD_LINKS_COUNT = "links_count";
        private static final String FIELD_INTERNAL_LINKS_COUNT = "internal_links_count";
        private static final String FIELD_SHARD_ID = "shard_id";

        @Override
        public UrlResultInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
            Boolean isError = rs.getBoolean(FIELD_IS_ERROR);
            UrlInfoStatusEnum status = isError ? UrlInfoStatusEnum.ERROR : UrlInfoStatusEnum.PROCESSING;
            YandexSearchShard searchShard = null;
            Integer shard = SqlUtil.getIntNullable(rs, FIELD_SHARD_ID);
            if (shard != null) {
                searchShard = YandexSearchShard.R.fromValueOrNull(shard);
            }
            return new UrlResultInfo(
                    rs.getLong(FIELD_HOST_ID),
                    new UrlRequestId(rs.getLong(FIELD_NUM)),
                    rs.getString(FIELD_URL),
                    SqlUtil.getStringNullable(rs, FIELD_NORMALIZED_URL),
                    rs.getTimestamp(FIELD_CREATE_DATE),
                    status,
                    SqlUtil.getBooleanNullable(rs, FIELD_IS_FROM_FAST_ROBOT),
                    SqlUtil.getBooleanNullable(rs, FIELD_SEARCHABLE_XML),
                    SqlUtil.getLongNullable(rs, FIELD_LINKS_COUNT),
                    SqlUtil.getLongNullable(rs, FIELD_INTERNAL_LINKS_COUNT),
                    SqlUtil.safeGetTimestamp(rs, FIELD_SEARCHABLE_XML_TIME),
                    searchShard,
                    SqlUtil.safeGetTimestamp(rs, FIELD_PENALTY_LAST_ACCESS),
                    rs.getBoolean(FIELD_FROM_SUPPORT));
        }
    };
}
