package ru.yandex.webmaster3.worker.importanturls;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.mutable.MutableLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskState;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskType;
import ru.yandex.webmaster3.core.worker.task.TaskResult;
import ru.yandex.webmaster3.storage.importanturls.ImportantUrlsYDao;
import ru.yandex.webmaster3.storage.spam.ISpamHostFilter;
import ru.yandex.webmaster3.storage.spam.SpamHostsYDao;
import ru.yandex.webmaster3.storage.util.yt.YtException;
import ru.yandex.webmaster3.storage.util.yt.YtNode;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.util.yt.YtService;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

/**
 * Периодический таск, сливающий данные по подпискам на важные страницы в YT
 *
 * @author Oleg Bazdyrev
 */
@Service("importantUrlsExportTask")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ImportantUrlsExportTask extends PeriodicTask<ImportantUrlsExportTask.TaskState> {

    private static final Logger log = LoggerFactory.getLogger(ImportantUrlsExportTask.class);

    private static final String FIELD_URL = "Url";
    private static final String FIELD_TIMESTAMP = "Timestamp";

    private final YtService ytService;
    private final ImportantUrlsYDao importantUrlsYDao;
    private final SpamHostsYDao spamHostsYDao;
    // куда сливать
    @Value("${webmaster3.worker.yt.importantUrls.outTable.path}")
    private final YtPath outputYtTablePath;
    private final ISpamHostFilter fastSpamHostFilter;

    @Override
    public Result run(UUID runId) throws Exception {
        // затянем все спамные хосты
        Set<WebmasterHostId> spamHostIds = new HashSet<>();
        spamHostsYDao.getAllHosts(spamHostIds::add);

        MutableLong exportedCount = new MutableLong();
        MutableLong bannedCount = new MutableLong();
        MutableLong totalCount = new MutableLong();

        ytService.inTransaction(outputYtTablePath).execute(cypressService -> {
            log.info("Uploading important URLS started, writing to YT-table {}", outputYtTablePath);
            // создадим табличку, затирая старые данные
            cypressService.create(outputYtTablePath, YtNode.NodeType.TABLE, true, null, true);
            // пишем новые данные
            cypressService.writeTable(outputYtTablePath, tableWriter -> {
                try {
                    importantUrlsYDao.listAll(importantUrl -> {
                        totalCount.increment();
                        if (totalCount.getValue() % 1000 == 0) {
                            log.info("Total {} important urls. Exported: {}, banned: {}",
                                    totalCount.getValue(), exportedCount.getValue(), bannedCount.getValue());
                        }
                        WebmasterHostId hostId = importantUrl.getHostId();
                        if (spamHostIds.contains(hostId) || fastSpamHostFilter.checkHost(hostId)) {
                            //log.warn("Ignoring url from spam host {}", hostId);
                            bannedCount.increment();
                            return;
                        }
                        String url = IdUtils.hostIdToUrl(hostId) + importantUrl.getRelativeUrl();
                        tableWriter.column(FIELD_URL, url);
                        tableWriter.column(FIELD_TIMESTAMP, importantUrl.getAddDate().getMillis() / 1000);
                        tableWriter.rowEnd();
                        exportedCount.increment();
                    });
                } catch (YtException e) {
                    throw new WebmasterException("Failed to write importants URLS for export",
                            new WebmasterErrorResponse.YTServiceErrorResponse(getClass(), e), e);
                }
            });
            log.info("Successfully uploaded important URLS to YT-table {}", outputYtTablePath);
            return true;
        });
        setState(new TaskState(exportedCount.getValue(), bannedCount.getValue(), totalCount.getValue()));
        return new Result(TaskResult.SUCCESS);
    }

    @Override
    public PeriodicTaskType getType() {
        return PeriodicTaskType.IMPORTANT_URLS_EXPORT;
    }

    @Override
    public TaskSchedule getSchedule() {
        return TaskSchedule.startByCron("54 17/30 * * * *");
    }

    public static class TaskState implements PeriodicTaskState {

        private final long exportedCount;
        private final long bannedCount;
        private final long totalCount;

        public TaskState(long exportedCount, long bannedCount, long totalCount) {
            this.exportedCount = exportedCount;
            this.bannedCount = bannedCount;
            this.totalCount = totalCount;
        }

        public long getExportedCount() {
            return exportedCount;
        }

        public long getBannedCount() {
            return bannedCount;
        }

        public long getTotalCount() {
            return totalCount;
        }
    }
}
