package ru.yandex.wmconsole.service;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.jetbrains.annotations.NotNull;
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.util.StringUtils;
import ru.yandex.common.util.db.OrderByClause;
import ru.yandex.wmconsole.data.VirusRecheckEnum;
import ru.yandex.wmconsole.data.info.BriefHostInfo;
import ru.yandex.wmconsole.data.info.HostDbHostInfo;
import ru.yandex.wmconsole.data.info.RecheckInfo;
import ru.yandex.wmconsole.data.info.VirusInfo;
import ru.yandex.wmconsole.data.partition.WMCPartition;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.service.AbstractDbService;

/**
 * @author Andrey Mima (amima@yandex-team.ru)
 */
public class VirusedHostService extends AbstractDbService {
    private static final Logger log = LoggerFactory.getLogger(VirusedHostService.class);

    private static final String FIELD_URL = "url";
    private static final String FIELD_LAST_TEST = "last_check";
    private static final String PARAM_URL = "url";
    private static final String PARAM_DATE = "date";

    private HostDbHostInfoService hostDbHostInfoService;

    private static final String UPDATE_RECHECK_UPDATE_QUERY =
            "UPDATE " +
                        "tbl_infected_hosts " +
                    "SET " +
                        "recheck = 1, " +
                        "recheck_time = NOW() " +
                    "WHERE " +
                        "host_id = ?";

    private static final String SELECT_RECHECK_INFO_QUERY =
            "SELECT " +
                        "recheck, recheck_time " +
                    "FROM " +
                        "tbl_infected_hosts " +
                    "WHERE " +
                        "host_id = ?";

    private static final String SELECT_VIRUSED_URLS_QUERY =
            "SELECT " +
                    "   url, name, last_check, urls_chain " +
                    "FROM " +
                    "   tbl_infected_urls " +
                    "WHERE " +
                    "   host_id = ? " +
                    "%s ";

    public void recheckHost(BriefHostInfo hostInfo) throws InternalException {
        log.debug("Rechecking host for viruses: " + hostInfo.getName());
        HostDbHostInfo hostDbHostInfo = hostDbHostInfoService.getHostDbHostInfo(hostInfo.getName());
        getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).update(
                UPDATE_RECHECK_UPDATE_QUERY, hostDbHostInfo.getHostDbHostId());
    }

    public List<VirusInfo> getVirusInfo(BriefHostInfo hostInfo, OrderByClause orderByClause) throws InternalException {
        HostDbHostInfo hostDbHostInfo = hostDbHostInfoService.getHostDbHostInfo(hostInfo.getName());
        List<VirusInfo> virusInfos = getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).orderableSelect(
                SELECT_VIRUSED_URLS_QUERY, VIRUS_INFO_MAPPER, orderByClause, hostDbHostInfo.getHostDbHostId());
        return filterNames(virusInfos);
    }

    @NotNull
    private List<VirusInfo> filterNames(List<VirusInfo> virusInfos) {
        List<VirusInfo> filtered = new ArrayList<>();
        if (!CollectionUtils.isEmpty(virusInfos)) {
            for (VirusInfo virusInfo : virusInfos) {
                String name = org.apache.commons.lang.StringUtils.trimToEmpty(virusInfo.getName());
                name = org.apache.commons.lang.StringUtils.replace(name, "\"", "");
                if (forbiddenVerdicts.contains(name.toLowerCase())) {
                    filtered.add(new VirusInfo(virusInfo.getUrl(), null, virusInfo.getLastCheck(), Collections.<String>emptyList()));
                } else {
                    filtered.add(virusInfo);
                }
            }
        }
        return filtered;
    }

    public RecheckInfo getRecheckInfo(BriefHostInfo hostInfo) {
        try {
            HostDbHostInfo hostDbHostInfo = hostDbHostInfoService.getHostDbHostInfo(hostInfo.getName());
            return getJdbcTemplate(new WMCPartition(hostDbHostInfo, null)).safeQueryForObject(
                    SELECT_RECHECK_INFO_QUERY, RECHECK_INFO_MAPPER, hostDbHostInfo.getHostDbHostId());
        } catch (InternalException e) {
            return null;
        }
    }

    public String getDefaultOrderByColumn() {
        return FIELD_URL;
    }

    public boolean getDefaultIsAscending() {
        return true;
    }

    public Map<String, String> getColumnNameCorrespondence() {
        Map<String, String> m = new LinkedHashMap<String, String>();
        m.put(PARAM_URL, FIELD_URL);
        m.put(PARAM_DATE, FIELD_LAST_TEST);
        return m;
    }

    private static final ParameterizedRowMapper<VirusInfo> VIRUS_INFO_MAPPER =
            new ParameterizedRowMapper<VirusInfo>() {
                private static final String FIELD_NAME = "name";
                private static final String FIELD_URL_CHAINS = "urls_chain";

                @Override
                public VirusInfo mapRow(ResultSet resultSet, int rowNum) throws SQLException {
                    String url = resultSet.getString(FIELD_URL);
                    String name = resultSet.getString(FIELD_NAME);
                    Date lastTest = resultSet.getTimestamp(FIELD_LAST_TEST);
                    String strVirusChains = resultSet.getString(FIELD_URL_CHAINS);
                    final List<String> virusChains;
                    if (resultSet.wasNull()) {
                        virusChains = Collections.emptyList();
                    } else {
                        virusChains = StringUtils.split(strVirusChains.substring(1, strVirusChains.length()-1), ",");
                    }
                    return new VirusInfo(url, name, lastTest, virusChains);
                }
            };

    private static final ParameterizedRowMapper<RecheckInfo> RECHECK_INFO_MAPPER =
            new ParameterizedRowMapper<RecheckInfo>() {
                private static final String FIELD_RECHECK = "recheck";
                private static final String FIELD_RECHECK_DATE = "recheck_time";

                @Override
                public RecheckInfo mapRow(ResultSet resultSet, int rowNum) throws SQLException {
                    VirusRecheckEnum recheck = VirusRecheckEnum.R.fromValueOrNull(resultSet.getInt(FIELD_RECHECK));
                    Date time = resultSet.getDate(FIELD_RECHECK_DATE);

                    return new RecheckInfo(recheck, time);
                }
            };

    @Required
    public void setHostDbHostInfoService(HostDbHostInfoService hostDbHostInfoService) {
        this.hostDbHostInfoService = hostDbHostInfoService;
    }

    private final Set<String> forbiddenVerdicts = new HashSet<>();
    {
        forbiddenVerdicts.add("AOL SuperBuddy exploit".toLowerCase());
        forbiddenVerdicts.add("AOL.ICQ exploit".toLowerCase());
        forbiddenVerdicts.add("AV detection".toLowerCase());
        forbiddenVerdicts.add("AskBar exploit".toLowerCase());
        forbiddenVerdicts.add("Aurigma Uploader exploit".toLowerCase());
        forbiddenVerdicts.add("Bad exploit".toLowerCase());
        forbiddenVerdicts.add("Bedep_payload".toLowerCase());
        forbiddenVerdicts.add("Bedep_payload2".toLowerCase());
        forbiddenVerdicts.add("CVE-2006-5745".toLowerCase());
        forbiddenVerdicts.add("CVE-2007-5659".toLowerCase());
        forbiddenVerdicts.add("CVE-2008-2992".toLowerCase());
        forbiddenVerdicts.add("CVE-2008-4261".toLowerCase());
        forbiddenVerdicts.add("CVE-2008-4728".toLowerCase());
        forbiddenVerdicts.add("CVE-2009-0927".toLowerCase());
        forbiddenVerdicts.add("CVE-2009-3031".toLowerCase());
        forbiddenVerdicts.add("CVE-2009-3033".toLowerCase());
        forbiddenVerdicts.add("CVE-2010-0108".toLowerCase());
        forbiddenVerdicts.add("CVE-2010-0188".toLowerCase());
        forbiddenVerdicts.add("CVE-2010-3189".toLowerCase());
        forbiddenVerdicts.add("CVE-2010-4452".toLowerCase());
        forbiddenVerdicts.add("CVE-2011-2140".toLowerCase());
        forbiddenVerdicts.add("CVE-2012-0507".toLowerCase());
        forbiddenVerdicts.add("CVE-2012-0507".toLowerCase());
        forbiddenVerdicts.add("CVE-2012-1889".toLowerCase());
        forbiddenVerdicts.add("CVE-2012-4681".toLowerCase());
        forbiddenVerdicts.add("CVE-2012-4681".toLowerCase());
        forbiddenVerdicts.add("CVE-2012-5070".toLowerCase());
        forbiddenVerdicts.add("CVE-2012-5076".toLowerCase());
        forbiddenVerdicts.add("CVE-2012-5076".toLowerCase());
        forbiddenVerdicts.add("CVE-2013-0422".toLowerCase());
        forbiddenVerdicts.add("CVE-2013-0422".toLowerCase());
        forbiddenVerdicts.add("CVE-2014-6332".toLowerCase());
        forbiddenVerdicts.add("Citrix IAC".toLowerCase());
        forbiddenVerdicts.add("DLoadClass exploit".toLowerCase());
        forbiddenVerdicts.add("DivXPlusWebPlayer.Open".toLowerCase());
        forbiddenVerdicts.add("EMET detection".toLowerCase());
        forbiddenVerdicts.add("Exploit".toLowerCase());
        forbiddenVerdicts.add("Exploits pack".toLowerCase());
        forbiddenVerdicts.add("FoxitReader.OpenFile".toLowerCase());
        forbiddenVerdicts.add("HP_Proto.Resample".toLowerCase());
        forbiddenVerdicts.add("Heap-Spray".toLowerCase());
        forbiddenVerdicts.add("ISUSWA exploit".toLowerCase());
        forbiddenVerdicts.add("JDeploy exploit".toLowerCase());
        forbiddenVerdicts.add("JSClassifier_Infected".toLowerCase());
        forbiddenVerdicts.add("JavaExploit".toLowerCase());
        forbiddenVerdicts.add("JavaHeur".toLowerCase());
        forbiddenVerdicts.add("JavaLocalExploit".toLowerCase());
        forbiddenVerdicts.add("JavaLocalStartProcess".toLowerCase());
        forbiddenVerdicts.add("JavaSandboxEscape".toLowerCase());
        forbiddenVerdicts.add("JavaSecurityOff".toLowerCase());
        forbiddenVerdicts.add("JavaStartProcess".toLowerCase());
        forbiddenVerdicts.add("JavaStartProcess".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousAppletX".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousAppletX".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousBlackhole".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousBlackhole".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousCrimeBoss".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousCrimeBoss".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousRedkit".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousRedkit".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousSofosFo".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousSofosFo".toLowerCase());
        forbiddenVerdicts.add("JavaSuspiciousWorld".toLowerCase());
        forbiddenVerdicts.add("JdtExploit".toLowerCase());
        forbiddenVerdicts.add("MSWorks7 exploit".toLowerCase());
        forbiddenVerdicts.add("OpenUrl".toLowerCase());
        forbiddenVerdicts.add("OpenWebFile".toLowerCase());
        forbiddenVerdicts.add("Palevo".toLowerCase());
        forbiddenVerdicts.add("Palevo2".toLowerCase());
        forbiddenVerdicts.add("PeachTree exploit".toLowerCase());
        forbiddenVerdicts.add("SetDocbase exploit".toLowerCase());
        forbiddenVerdicts.add("Suspicious".toLowerCase());
        forbiddenVerdicts.add("VT::apk::infected".toLowerCase());
        forbiddenVerdicts.add("VT::pe32::infected".toLowerCase());
        forbiddenVerdicts.add("WinMediaEncoder exploit".toLowerCase());
        forbiddenVerdicts.add("WinZip exploit".toLowerCase());
        forbiddenVerdicts.add("Winamp AOL Exploit".toLowerCase());
        forbiddenVerdicts.add("XMLHTTPRequest".toLowerCase());
        forbiddenVerdicts.add("Yahoo JukeBox exploit".toLowerCase());
        forbiddenVerdicts.add("YahooMessenger".toLowerCase());
        forbiddenVerdicts.add("YahooMessengerWcViewer exploit".toLowerCase());
        forbiddenVerdicts.add("Yandex/Blacklisted".toLowerCase());
        forbiddenVerdicts.add("Yandex/BlacklistedUnknownTag".toLowerCase());
        forbiddenVerdicts.add("Yandex/EKLP.1".toLowerCase());
        forbiddenVerdicts.add("Yandex/Exploit".toLowerCase());
        forbiddenVerdicts.add("Yandex/ExploitElementCount".toLowerCase());
        forbiddenVerdicts.add("Yandex/MalRedir".toLowerCase());
        forbiddenVerdicts.add("Yandex/RegExpBlacklistedTag".toLowerCase());
        forbiddenVerdicts.add("Yandex/RegexpBlacklisted".toLowerCase());
        forbiddenVerdicts.add("Yandex/RegexpBlacklistedUnknownTag".toLowerCase());
        forbiddenVerdicts.add("Yandex/SwfExpTag".toLowerCase());
        forbiddenVerdicts.add("Yndx/MegaVirus".toLowerCase());
        forbiddenVerdicts.add("Zango exploit".toLowerCase());
        forbiddenVerdicts.add("Zenturi exploit".toLowerCase());
        forbiddenVerdicts.add("fl11_bad_length".toLowerCase());
        forbiddenVerdicts.add("hcp exploit".toLowerCase());
        forbiddenVerdicts.add("infected_manual".toLowerCase());
        forbiddenVerdicts.add("vgxBOF".toLowerCase());
        forbiddenVerdicts.add("vgxIOF".toLowerCase());
        forbiddenVerdicts.add("vgx_bad_length".toLowerCase());
        forbiddenVerdicts.add("yuspicious".toLowerCase());
    }
}
