package ru.yandex.webmaster3.worker.mirrors;

import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import com.google.common.collect.ImmutableMap;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import ru.yandex.webmaster3.storage.clickhouse.TableType;
import ru.yandex.webmaster3.storage.jupiter.JupiterUtils;
import ru.yandex.webmaster3.storage.mirrors.dao.MainMirrorsCHDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHField;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHTable;
import ru.yandex.webmaster3.storage.util.yt.YtNode;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.yql.YqlFunctions;
import ru.yandex.webmaster3.storage.yql.YqlQueryBuilder;
import ru.yandex.webmaster3.storage.ytimport.YtClickhouseDataLoad;
import ru.yandex.webmaster3.storage.ytimport.YtClickhouseDataLoadState;
import ru.yandex.webmaster3.storage.ytimport.YtClickhouseDataLoadType;
import ru.yandex.webmaster3.worker.TaskSchedule;

import static ru.yandex.webmaster3.storage.ytimport.YtClickhouseDataLoadType.ALL_MIRRORS;

@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ImportAllMirrorsTask extends AbstractImportJupiterExportTask {

    private static final int LINES_COUNT = 512;

    private static final Map<String, String> SOURCE_EXPRESSIONS = new ImmutableMap.Builder<String, String>()
            .put(MainMirrorsCHDao.F.HOST, "$url2HostId(`Host`)")
            .put(MainMirrorsCHDao.F.MAIN_HOST, "$url2HostId(`MainHost`)")
            .build();
    private static final String SUFFIX_TO_MIRRORS_TABLE = "/mirrors/mirrors";

    @Value("${webmaster3.worker.uploadWebmasterHostsTask.arnold.export.path}")
    public YtPath allHostsWebmasterTable;

    @Override
    protected int getShardsCount() {
        return 1;
    }

    @Override
    protected YtClickhouseDataLoad init(YtClickhouseDataLoad latestImport) throws Exception {
        return ytService.inTransaction(tablePath).query(cypressService -> {
            String baseState = getBaseState();

            final YtPath priemkaExportTablePath = getPathToTableInExport(baseState, getJupiterSuffix());

            final YtNode node = cypressService.getNode(priemkaExportTablePath);
            final String data = Objects.requireNonNullElse(latestImport.getData(), "0");


            baseState = JupiterUtils.getStateWithOnlyDigits(baseState);
            if (!data.equals(baseState)) {
                final LocalDate date = node.getUpdateTime().toLocalDate();

                log.info("previously base state -- {}, current base state -- {}",
                        data, baseState);

                return latestImport.withData(baseState)
                        .withSourceTable(priemkaExportTablePath, date, date);
            }
            // ничего нового
            log.info("base state -- {} is actual", baseState);
            return latestImport.withState(YtClickhouseDataLoadState.DONE);
        });
    }

    @Override
    // импортим главные зеркала для всех хостов вебмастера, которые сами не являются главным зеркалом
    protected YqlQueryBuilder prepareIntermediateTable(final YtClickhouseDataLoad imprt) {
        final String dateString = IN_YQL_QUERY_DATE_FORMATTER.print(imprt.getDateTo());
        final YtPath sourceTable = imprt.getSourceTable();
        final String fields = getTable().getFields().stream()
                .map(CHField::getName)
                .filter(chField -> !chField.equals(MainMirrorsCHDao.F.DATE))
                .map(SOURCE_EXPRESSIONS::get)
                .collect(Collectors.joining(" || '\\t' || ", "('" + dateString + "'|| '\\t' || ", " || '\\n')"));

        final Map<String, String> values = Map.of(
                "jupiterMirrors", sourceTable.toYqlPath(),
                "INTERMEDIATE_TABLE", INTERMEDIATE_TABLE,
                "LINES_COUNT", String.valueOf(LINES_COUNT),
                "SHARDS_COUNT", String.valueOf(getShardsCount()),
                "fields", fields,
                "KEY_FOR_SHARD", "Digest::Fnv64($url2HostId(`Host`))"
        );
        StrSubstitutor sub = new StrSubstitutor(values);

        String template = "PRAGMA yt.MaxRowWeight = '128M';\n" +
                "PRAGMA yt.InferSchema = '1';\n" +
                "INSERT INTO ${INTERMEDIATE_TABLE} " +
                "SELECT ShardId, RowId, Compress::Gzip(String::JoinFromList(AGGREGATE_LIST(data), ''), 6) as data " +
                "FROM (\n" +
                "SELECT ((${KEY_FOR_SHARD}) % ${SHARDS_COUNT}) as ShardId, ((" +
                "${KEY_FOR_SHARD} / ${SHARDS_COUNT}) % ${LINES_COUNT} ) as RowId, ${fields} as data " +
                "FROM ${jupiterMirrors} as jupiterMirrors  WHERE `Host` != `MainHost`) \n" +
                "GROUP BY ShardId,RowId;\n COMMIT;\n\n";

        String query = sub.replace(template);

        return YqlQueryBuilder.newBuilder()
                .cluster(sourceTable)
                .appendFDefinition(YqlFunctions.URL_2_HOST_ID)
                .appendText(query);
    }

    @Override
    protected YtClickhouseDataLoad createDistributedTables(YtClickhouseDataLoad imprt) throws Exception {
        return imprt.withNextState();
    }

    @Override
    protected CHTable getTable() {
        return MainMirrorsCHDao.TABLE_ALL_MIRRORS;
    }

    @Override
    protected TableType getTableType() {
        return TableType.ALL_MIRRORS;
    }

    @Override
    protected YtClickhouseDataLoadType getImportType() {
        return ALL_MIRRORS;
    }

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

    @Override
    protected String getJupiterSuffix() {
        return SUFFIX_TO_MIRRORS_TABLE;
    }

    @Override
    protected @NonNull String getBaseState() {
        return jupiterUtils.getCurrentState();
    }
}
