package ru.yandex.webmaster3.worker.importanturls;

import java.util.UUID;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

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.HostsWithImportantUrlsYDao;
import ru.yandex.webmaster3.storage.util.yt.YtColumn;
import ru.yandex.webmaster3.storage.util.yt.YtException;
import ru.yandex.webmaster3.storage.util.yt.YtNode;
import ru.yandex.webmaster3.storage.util.yt.YtNodeAttributes;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.util.yt.YtSchema;
import ru.yandex.webmaster3.storage.util.yt.YtService;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Repository("uploadWebmasterHostsWithImportantUrlsTask")
public class UploadWebmasterHostsWithImportantUrlsTask extends PeriodicTask<UploadWebmasterHostsWithImportantUrlsTask.TaskState> {

    private static final int CASSANDRA_BATCH_SIZE = 1024;
    private static final String YT_TABLE_NAME = "webmaster-hosts-with-important-urls";

    private TaskState state;
    private final HostsWithImportantUrlsYDao hostsWithImportantUrlsYDao;
    private final YtService ytService;

    @Value("${webmaster3.worker.yt.importantUrls.root.path}")
    private YtPath arnoldImportantUrlsRoot;

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

    @Override
    public Result run(UUID runId) throws Exception {
        state = new UploadWebmasterHostsWithImportantUrlsTask.TaskState();
        setState(state);

        runUploadHostsWithImportantUrls(arnoldImportantUrlsRoot);

        return new Result(TaskResult.SUCCESS);
    }

    private void runUploadHostsWithImportantUrls(YtPath exportPath) throws YtException {
        final YtPath tablePath = YtPath.path(arnoldImportantUrlsRoot, YT_TABLE_NAME);
        try {
            ytService.inTransaction(arnoldImportantUrlsRoot).execute(cypressService -> {
                        if (!cypressService.exists(tablePath)) {
                            YtNodeAttributes attributes = new YtNodeAttributes().setSchema(F.SCHEMA);
                            cypressService.create(tablePath, YtNode.NodeType.TABLE, true, attributes);
                        }

                        log.info("Write hosts with important urls from cassandra to yt...");
                        cypressService.writeTable(tablePath, tw -> {
                            hostsWithImportantUrlsYDao.iterateAllHosts(hostId -> {
                                F.HOST_ID.set(tw, hostId.toStringId());
                                F.HOST_URL.set(tw, IdUtils.hostIdToUrl(hostId));
                                tw.rowEnd();
                                state.hostsWithImportantUrlsLoaded++;
                            });
                        });
                        log.info("Hosts with important urls write from cassandra to yt table {}", state.hostsWithImportantUrlsLoaded);

                        return true;
                    }
            );
        } catch (YtException e) {
            throw new YtException("Unable upload hosts with important urls to " + exportPath, e);
        }

        log.info("Hosts with important urls uploaded into {}", exportPath);
    }

    private interface F {
        YtSchema SCHEMA = new YtSchema();
        YtColumn<String> HOST_ID = SCHEMA.addColumn("host_id", YtColumn.Type.STRING);
        YtColumn<String> HOST_URL = SCHEMA.addColumn("host_url", YtColumn.Type.STRING);
    }

    @Override
    public TaskSchedule getSchedule() {
        return TaskSchedule.startByCron("0 50 0/2 * * *");
    }

    @Getter
    public static class TaskState implements PeriodicTaskState {
        private long hostsWithImportantUrlsLoaded = 0;
    }
}
