package ru.yandex.webmaster3.worker.radar;

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

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.metrika.radar.HostAllowedMetrica;
import ru.yandex.webmaster3.core.metrika.radar.HostMetricaAppIdState;
import ru.yandex.webmaster3.core.metrika.radar.MetricaAppIdBindingStateEnum;
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.util.ydb.exception.WebmasterYdbException;
import ru.yandex.webmaster3.storage.radar.RadarHostsAllowedMetrikaYDao;
import ru.yandex.webmaster3.storage.radar.appId.HostMetricaAppIdYDao;
import ru.yandex.webmaster3.storage.util.yt.TableWriter;
import ru.yandex.webmaster3.storage.util.yt.YtException;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.util.yt.YtService;
import ru.yandex.webmaster3.storage.util.yt.YtTableData;
import ru.yandex.webmaster3.storage.util.yt.YtTransactionService;
import ru.yandex.webmaster3.storage.util.yt.YtUtils;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

/**
 * Created by ifilippov5 on 15.03.18.
 */
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ExportHostsAllowedMetrikaPeriodicTask extends PeriodicTask<ExportHostsAllowedMetrikaPeriodicTask.TaskState> {

    private static final String TABLE_SCHEMA = "[" +
            "{'name': '" + YtRow.F_HOST_URL + "', 'type': 'string'}, " +
            "{'name': '" + YtRow.F_ALLOWED_METRIKA + "', 'type': 'boolean'}, " +
            "{'name': '" + YtRow.F_ALLOWED_APP_METRIKA + "', 'type': 'boolean'}, " +
            "{'name': '" + YtRow.F_APP_ID_LIST + "', 'type': 'any'}]";

    private final RadarHostsAllowedMetrikaYDao radarHostsAllowedMetrikaYDao;
    private final HostMetricaAppIdYDao hostMetricaAppIdYDao;
    private final YtService ytService;

    @Value("${external.yt.service.arnold.root.default}/export/radar")
    private YtPath workDirArnold;
    @Value("${external.yt.service.hahn.root.default}/export/radar")
    private YtPath workDirHahn;
    @Value("${webmaster3.worker.metrika.radar.exportTableName}")
    private String tableName;

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

        process(workDirArnold);
        process(workDirHahn);

        return new Result(TaskResult.SUCCESS);
    }

    private void process(YtPath workDir) throws Exception {
        YtPath tablePath = YtPath.path(workDir, tableName);
        YtTableData table = null;
        try {
            table = ytService.prepareTableData(tablePath.getName(), this::uploadFromYdb);

            YtTransactionService.TransactionProcess process = new YtUtils.TransactionWriterBuilder(tablePath, table)
                    .withSchema(TABLE_SCHEMA)
                    .build();

            YtUtils.TransactionExecutor writer = new YtUtils.TransactionExecutor(ytService, workDir);
            writer.execute(process);
        } finally {
            if (table != null) {
                table.delete();
            }
        }
    }

    private void uploadFromYdb(TableWriter tw) {
        try {
            for (HostAllowedMetrica allowedHost : radarHostsAllowedMetrikaYDao.getAllFlags()) {
                writeToTable(tw, allowedHost, hostMetricaAppIdYDao.getAll(allowedHost.getHostUrl())
                        .stream()
                        .filter(state -> state.getBindingState() == MetricaAppIdBindingStateEnum.CONFIRMED)
                        .map(HostMetricaAppIdState::getAppId)
                        .collect(Collectors.toList()));
            }
        } catch (WebmasterYdbException e) {
            throw new WebmasterException("Unable to get all allowed flags",
                    new WebmasterErrorResponse.YDBErrorResponse(getClass(), e), e);
        }
    }

    private void writeToTable(TableWriter tw, HostAllowedMetrica allowedHost, List<Long> appIds) {
        tw.column(YtRow.F_HOST_URL, allowedHost.getHostUrl());
        tw.column(YtRow.F_ALLOWED_METRIKA, allowedHost.isAllowedMetrica());
        tw.column(YtRow.F_ALLOWED_APP_METRIKA, allowedHost.isAllowedAppMetrica());
        tw.columnObject(YtRow.F_APP_ID_LIST, appIds);
        try {
            tw.rowEnd();
        } catch (YtException e) {
            throw new RuntimeException(e);
        }
    }

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

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

    private static class YtRow {
        static final String F_HOST_URL = "host_url";
        static final String F_ALLOWED_METRIKA = "allowed_metrica";
        static final String F_ALLOWED_APP_METRIKA = "allowed_app_metrica";
        static final String F_APP_ID_LIST = "app_id_list";
    }

    public static class TaskState implements PeriodicTaskState {
    }
}
