package ru.yandex.webmaster3.worker.sitetree;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

import lombok.RequiredArgsConstructor;
import org.joda.time.DateTime;
import org.joda.time.Instant;
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.util.IdUtils;
import ru.yandex.webmaster3.core.util.TimeUtils;
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.host.CommonDataType;
import ru.yandex.webmaster3.storage.host.dao.HostsYDao;
import ru.yandex.webmaster3.storage.settings.SettingsService;
import ru.yandex.webmaster3.storage.util.yt.YtColumn;
import ru.yandex.webmaster3.storage.util.yt.YtCypressService;
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.storage.util.yt.YtTableData;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

/**
 * User: azakharov
 * Date: 12.03.15
 * Time: 14:34
 */
@Component("uploadWebmasterHostsSpecificTask")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UploadWebmasterHostsSpecificTask extends PeriodicTask<UploadWebmasterHostsSpecificTask.TaskState> {
    private static final Logger log = LoggerFactory.getLogger(UploadWebmasterHostsSpecificTask.class);

    private static final int CASSANDRA_BATCH_SIZE = 1024;

    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 HostsYDao hostsYDao;
    private final YtService ytService;
    private final SettingsService settingsService;
    @Value("${webmaster3.worker.uploadWebmasterHostsTask.hahn.export.path}")
    private YtPath hahnHostsExportPath;
    @Value("${webmaster3.worker.uploadWebmasterHostsTask.arnold.export.path}")
    private YtPath arnoldHostsExportPath;

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

    @Override
    public Result run(UUID runId) throws Exception {
        final TaskState state = new TaskState();
        setState(state);
        log.info("Download hosts...");
        String nowMillis = String.valueOf(DateTime.now().getMillis());
        YtTableData tableData = ytService.prepareTableData("webmaster-hosts", tableWriter -> {
            hostsYDao.forEachHost(hostId -> {
                String host = IdUtils.hostIdToUrl(hostId);
                F.KEY.set(tableWriter, host);
                F.VALUE.set(tableWriter, "");
                F.HOST.set(tableWriter, host);
                F.PATH.set(tableWriter, "/");
                tableWriter.rowEnd();
                state.hostLoaded++;
            });
        });
        log.info("Hosts downloaded {}", state.hostLoaded);

        final String tempName = "webmaster-hosts_" + TimeUtils.DF_YYYYMMDD_HHMMSS.print(Instant.now());

        ytService.inTransaction(hahnHostsExportPath)
                .withTimeout(5, TimeUnit.MINUTES)
                .execute(cypressService -> {
                    uploadToYt(cypressService, hahnHostsExportPath, tempName, tableData);
                    return true;
                });

        ytService.inTransaction(arnoldHostsExportPath)
                .withTimeout(5, TimeUnit.MINUTES)
                .execute(cypressService -> {
                    uploadToYt(cypressService, arnoldHostsExportPath, tempName, tableData);
                    return true;
                });
        state.hostSentToHahn = state.hostLoaded;
        state.hostSentToArnold = state.hostLoaded;
        settingsService.update(CommonDataType.LAST_EXPORTED_HOSTS, nowMillis);
        return new Result(TaskResult.SUCCESS);
    }

    private void uploadToYt(YtCypressService cypressService, YtPath exportPath, String tempTableName, YtTableData tableData)
    {
        YtPath tmpTablePath = YtPath.path(exportPath.getParent(), tempTableName);
        log.info("Tmp path: {}", tmpTablePath);
        log.info("Table path: {}", exportPath);

        cypressService.remove(tmpTablePath, false, true);
        YtNodeAttributes attributes = new YtNodeAttributes().setSchema(F.SCHEMA);
        cypressService.create(tmpTablePath, YtNode.NodeType.TABLE, true, attributes, true);

        cypressService.writeTable(tmpTablePath, tableData);
        YtOperationId sort = cypressService.sort(tmpTablePath, tmpTablePath, F.HOST.getName(), F.PATH.getName());
        cypressService.waitFor(sort);
        cypressService.move(tmpTablePath, exportPath, true, true);
    }

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

    public static class TaskState implements PeriodicTaskState {
        private long hostLoaded = 0;
        private long hostSentToHahn = 0;
        private long hostSentToArnold = 0;

        public long getHostLoaded() {
            return hostLoaded;
        }

        public long getHostSentToHahn() {
            return hostSentToHahn;
        }

        public long getHostSentToArnold() {
            return hostSentToArnold;
        }
    }
}
