package ru.yandex.webmaster3.worker.sitetree;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

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

import ru.yandex.webmaster3.core.data.WebmasterHostId;
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.user.dao.UserSiteTreeYDao;
import ru.yandex.webmaster3.storage.util.yt.TableWriter;
import ru.yandex.webmaster3.storage.util.yt.YtColumn;
import ru.yandex.webmaster3.storage.util.yt.YtNode;
import ru.yandex.webmaster3.storage.util.yt.YtNodeAttributes;
import ru.yandex.webmaster3.storage.util.yt.YtOperationId;
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;

/**
 * User: azakharov
 * Date: 02.10.14
 * Time: 19:14
 */
@Component("uploadUserNodesTask")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UploadUserNodesTask extends PeriodicTask<PeriodicTaskState> {
    private static final Logger log = LoggerFactory.getLogger(UploadUserNodesTask.class);
    private static final String TABLE_NAME = "sitetree";

    private interface F {
        YtSchema SCHEMA = new YtSchema();
        YtColumn<String> KEY = SCHEMA.addColumn("key", YtColumn.Type.STRING);
        YtColumn<String> VALUE = SCHEMA.addColumn("value", YtColumn.Type.STRING);
        YtColumn<String> HOST = SCHEMA.addColumn("Host", YtColumn.Type.STRING);
        YtColumn<String> PATH = SCHEMA.addColumn("Path", YtColumn.Type.STRING);
    }

    private final UserSiteTreeYDao userSiteTreeYDao;
    private final YtService ytService;
    @Value("${external.yt.service.arnold.root.default}/user")
    private YtPath tableDir;

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

    @Override
    public TaskSchedule getSchedule() {
        return TaskSchedule.startByCron("0 30 10 * * *");
    }

    @Override
    public Result run(UUID runId) throws Exception {
        MutableLong hostCounter = new MutableLong(0L);

        YtPath ytTableName = YtPath.path(tableDir, TABLE_NAME);
        log.info("Upload user nodes to: {}", ytTableName);

        ytService.inTransaction(ytTableName).execute(cypressService -> {
            YtNodeAttributes attributes = new YtNodeAttributes().setSchema(F.SCHEMA);
            cypressService.remove(ytTableName, true, true);
            cypressService.create(ytTableName, YtNode.NodeType.TABLE, true, attributes, true);
            cypressService.writeTable(ytTableName, tableWriter -> {
                MutableObject<WebmasterHostId> lastHost = new MutableObject<>();
                List<String> names = new ArrayList<>();
                userSiteTreeYDao.forEach(cons -> {
                    WebmasterHostId prevHost = lastHost.getValue();
                    if (!cons.getLeft().equals(prevHost)) {
                        uploadHostAndNodes(prevHost, names, tableWriter, hostCounter);
                        names.clear();
                        lastHost.setValue(cons.getLeft());
                    }
                    names.add(cons.getRight());
                });
                uploadHostAndNodes(lastHost.getValue(), names, tableWriter, hostCounter);
            });
            YtOperationId sort = cypressService.sort(ytTableName, ytTableName, F.HOST.getName(), F.PATH.getName());
            cypressService.waitFor(sort);
            return true;
        });

        log.debug("hosts written to MR: {}", hostCounter.getValue());

        return new Result(TaskResult.SUCCESS);
    }

    private void uploadHostAndNodes(WebmasterHostId hostId, List<String> names, TableWriter tableWriter,
                                    MutableLong hostCounter) {
        if (names.isEmpty()) {
            return;
        }
        String value = names.stream()
                .map(v -> v.startsWith("/") ? v : "/" + v)
                .collect(Collectors.joining("\t"));
        String host = IdUtils.hostIdToUrl(hostId);
        F.HOST.set(tableWriter, host);
        F.PATH.set(tableWriter, "/");
        F.KEY.set(tableWriter, host);
        F.VALUE.set(tableWriter, value);
        tableWriter.rowEnd();
        hostCounter.add(1);
    }
}
