package ru.yandex.webmaster.periodic.host;

import java.net.URL;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.webmaster.common.host.AddHostEventData;
import ru.yandex.webmaster.common.host.HostEvent;
import ru.yandex.webmaster.common.host.HostEventService;
import ru.yandex.webmaster.common.host.HostEventType;
import ru.yandex.wmconsole.data.AddUrlRequest;
import ru.yandex.wmconsole.data.HostInfoStatusEnum;
import ru.yandex.wmconsole.data.info.HostDbHostInfo;
import ru.yandex.wmconsole.data.info.HostStatusInfo;
import ru.yandex.wmconsole.service.AddUrlLogService;
import ru.yandex.wmconsole.service.AddUrlService;
import ru.yandex.wmconsole.service.HostDbHostInfoService;
import ru.yandex.wmconsole.service.HostInfoService;
import ru.yandex.wmconsole.service.dao.TblHostInfoDao;
import ru.yandex.wmconsole.service.error.WMCUserProblem;
import ru.yandex.wmtools.common.error.IUserProblem;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.servantlet.AbstractServantlet;
import ru.yandex.wmtools.common.util.URLUtil;

/**
 * @author aherman
 */
public class AddUrlForNewHostsTask extends AbstractHostEventTask {
    private static final Logger log = LoggerFactory.getLogger(AddUrlForNewHostsTask.class);

    private static final String CALLADDURL_ERROR = "CALLADDURL_PROBLEM";
    private static final String SKIP_ADD_URL = "SKIP_ADD_URL ";
    private static final int NEW_EVENTS_BATCH_SIZE = 1024;

    private AddUrlService addUrlService;
    private AddUrlLogService addUrlLogService;
    private HostEventService hostEventService;
    private HostInfoService hostInfoService;
    private HostDbHostInfoService hostDbHostInfoService;

    private TblHostInfoDao tblHostInfoDao;

    @Override
    protected void process(HostEvent hostEvent) throws InternalException, UserException {
        log.info("AddUrl for new host: event=" + hostEvent);
        String hostname = hostInfoService.getHostNameByHostId(hostEvent.getHostId());
        if (hostname == null) {
            log.warn("Host not exists: event=" + hostEvent);
            return;
        }
        if (URLUtil.isSpamerDomain(hostname)) {
            log.debug(SKIP_ADD_URL + hostname);
            return;
        }

        HostDbHostInfo hostDbHostInfo = hostDbHostInfoService.getHostDbHostInfo(hostname);
        if (hostDbHostInfo == null) {
            log.warn("Host not exists: event=" + hostEvent);
            return;
        }

        AddHostEventData data = hostEventService.getEventData(hostEvent, AddHostEventData.class);

        callAddUrl(AbstractServantlet.prepareUrl(hostname, false), hostDbHostInfo, hostEvent.getEventByUserId(), data);

        log.info("AddUrl for new host complete: event=" + hostEvent);
    }

    private void callAddUrl(final URL url, final HostDbHostInfo hostDbHostInfo,
            final Long uid,
            AddHostEventData data)
    {
        boolean successful = true;
        IUserProblem problem = null;

        String userIp = null;
        String  frontendIp = null;
        String yaDomain = null;
        String yandexUid = null;

        if (data != null) {
            userIp = data.getUserIp();
            frontendIp = data.getFrontendIp();
            yaDomain = data.getYaDomain();
            yandexUid = data.getYandexUid();
        }

        AddUrlRequest addUrlRequest = new AddUrlRequest(url, frontendIp, uid, userIp, yandexUid);
        try {
            addUrlService.addUrl(addUrlRequest, false, yaDomain, true);
        } catch (UserException e) {
            log.info(CALLADDURL_ERROR);
            successful = false;
            problem = e.getProblem();
        } catch (InternalException e) {
            log.info(CALLADDURL_ERROR);
            successful = false;
            log.info("callAddUrl: InternalException (" + e + ")");
        } catch (Exception e) {
            log.error(CALLADDURL_ERROR + e.getMessage());
            successful = false;
        } finally {
            if (!successful) {
                addUrlLogService.logUrlError(addUrlRequest);
            }
            try {
                saveAddUrlStatus(hostDbHostInfo, successful, problem);
            } catch (InternalException e) {
                log.error("callAddUrl: cannot save addUrl status (" + e + ")");
            }
        }
    }

    private void saveAddUrlStatus(HostDbHostInfo hostDbHostInfo, boolean successful, IUserProblem problem)
            throws InternalException {
        HostInfoStatusEnum status = HostInfoStatusEnum.WAITING;
        if (!successful) {
            if (WMCUserProblem.URL_IS_DISALLOWED_IN_ROBOTS_TXT.equals(problem)) {
                status = HostInfoStatusEnum.ROBOTS_TXT;
            } else if (problem == null) {
                status = HostInfoStatusEnum.INTERNAL_ERROR;
            }
        }

        if (WMCUserProblem.URL_IS_ALREADY_INDEXED.equals(problem) || hostInfoService.isIndexed(hostDbHostInfo)) {
            log.info("Url is already indexed, won't saving status");
            return;
        }

        saveHostInfoStatus(hostDbHostInfo, status);
    }

    private void saveHostInfoStatus(HostDbHostInfo hostDbHostInfo, HostInfoStatusEnum status) throws InternalException {
        HostStatusInfo oldStatusInfo = hostInfoService.getHostInfoStatus(hostDbHostInfo);
        HostInfoStatusEnum oldStatusEnum = oldStatusInfo != null ? oldStatusInfo.getCalculatedHostInfoStatus() : null;

        if (oldStatusEnum == null ||
                HostInfoStatusEnum.WAITING == oldStatusEnum && !(HostInfoStatusEnum.WAITING == status))
        {
            log.debug("saveAddUrlStatus: save status " + status + " for host with id = " + hostDbHostInfo
                    .getHostDbHostId());
            tblHostInfoDao.addHostInfoOrUpdateStatus(hostDbHostInfo, status);
        }
    }

    @Override
    protected int getRetryCount() {
        return 2;
    }

    @Override
    protected HostEventType getEventType() {
        return HostEventType.HOST_ADDED;
    }

    @Override
    protected int getBatchSize() {
        return NEW_EVENTS_BATCH_SIZE;
    }

    @Override
    protected HostEventTaskType getTaskType() {
        return HostEventTaskType.NEW_HOST_ADD_URL;
    }

    @Required
    public void setAddUrlService(AddUrlService addUrlService) {
        this.addUrlService = addUrlService;
    }

    @Required
    public void setHostInfoService(HostInfoService hostInfoService) {
        this.hostInfoService = hostInfoService;
    }

    @Required
    public void setAddUrlLogService(AddUrlLogService addUrlLogService) {
        this.addUrlLogService = addUrlLogService;
    }

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

    @Required
    public void setHostEventService(HostEventService hostEventService) {
        this.hostEventService = hostEventService;
    }

    @Required
    public void setTblHostInfoDao(TblHostInfoDao tblHostInfoDao) {
        this.tblHostInfoDao = tblHostInfoDao;
    }
}
