package ru.yandex.webmaster3.worker.niche2;

import java.util.Collections;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.joda.time.Duration;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import ru.yandex.webmaster3.core.worker.task.PeriodicTaskType;
import ru.yandex.webmaster3.storage.clickhouse.TableType;
import ru.yandex.webmaster3.storage.niche2.Niche2RivalsCHDao;
import ru.yandex.webmaster3.storage.niche2.Niche2RivalsCHDao.F;
import ru.yandex.webmaster3.storage.util.clickhouse2.AbstractClickhouseDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHPrimitiveType;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHTable;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseHost;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseQueryContext;
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.YtClickhouseDataLoadType;
import ru.yandex.webmaster3.worker.TaskSchedule;
import ru.yandex.webmaster3.worker.turbo.AbstractYqlPrepareImportTask;

@Slf4j
public class ImportNicheRivalsReportTask extends AbstractYqlPrepareImportTask {
    private static final int LINES_COUNT = 512;
    private static final String TABLE_NAME = Niche2RivalsCHDao.TABLE_NAME;
    private static final DateTimeFormatter PARTITION_MONTH_FORMAT = DateTimeFormat.forPattern("yyyyMM");
    private static final String MERGE_TABLE_QUERY = """
            ALTER TABLE ${DB}.${FULL_TABLE} DELETE WHERE date = '${DATE}'

            ALTER TABLE ${DB}.${FULL_TABLE} ATTACH PARTITION '${MONTH}' FROM ${DB}.${TEMP_TABLE}

            DROP TABLE ${DB}.${TEMP_TABLE};""";

    private static final CHTable TABLE = CHTable.builder()
            .database(AbstractClickhouseDao.DB_WEBMASTER3_NICHE)
            .name("tmp_" + TABLE_NAME + "_%s")
            .partitionBy("toYYYYMM(date)")
            .sharded(true)
            .keyField(F.DATE, CHPrimitiveType.Date)
            .keyField(F.DOMAIN, CHPrimitiveType.String)
            .keyField(F.GROUP_ID, CHPrimitiveType.String)
            .keyField(F.RIVAL_TYPE, CHPrimitiveType.String)
            .keyField(F.REGION_ID, CHPrimitiveType.Int64)
            .field(F.RIVAL, CHPrimitiveType.String)
            .field(F.VISIBILITY, CHPrimitiveType.Float64)
            .build();

    @Override
    protected YtClickhouseDataLoad init(YtClickhouseDataLoad imprt) throws Exception {
        log.info("CREATING QUERY: {}", ImportNicheRivalsReportTask.TABLE.createReplicatedMergeTree(-1, ""));
        return initByTableName(imprt);
    }

    @Override
    protected int getShardsCount() {
        return clickhouseServer.getShardsCount();
    }

    @Override
    protected YqlQueryBuilder prepareIntermediateTable(YtClickhouseDataLoad imprt) {
        log.info("CREATING QUERY: {}", ImportNicheRivalsReportTask.TABLE.createReplicatedMergeTree(-1, ""));
        String tableDate = imprt.getSourceTable().getName();
        return YqlQueryBuilder.newBuilder()
                .cluster(tablePath)
                .appendFDefinition(YqlFunctions.ESCAPE)
                .appendText("PRAGMA yt.MaxRowWeight = '128M';\n")
                .appendText("PRAGMA yt.DefaultMemoryLimit = '4G';\n\n")
                .appendText("INSERT INTO " + INTERMEDIATE_TABLE)
                .appendText("SELECT ShardId, RowId, Compress::Gzip(String::JoinFromList(AGGREGATE_LIST(data), ''), 6)" +
                        " as data FROM (\n")
                .appendText("SELECT (Digest::Fnv64(Domain) % " + getShardsCount() + ") as ShardId,")
                .appendText("((Digest::Fnv64(Domain || RivalType) / " + getShardsCount() + ") % " + LINES_COUNT + ") " +
                        "as " +
                        "RowId,")
                .appendText("('" + tableDate + "' || '\\t' ||\n")
                .appendText("Domain || '\\t' ||\n")
                .appendText("$escape(GroupId) || '\\t' ||\n")
                .appendText("RivalType || '\\t' ||\n")
                .appendText("CAST(RegionId as String) || '\\t' ||\n")
                .appendText("$escape(nvl(Rival, 'null')) || '\\t' " +
                        "||\n")
                .appendText("CAST(Visibility as String) || '\\n') " +
                        "as data\n")
                .appendText("FROM (\n")
                .appendText("SELECT Domain, " +
                        "GroupId, " +
                        "RivalType, " +
                        "-1 as RegionId, " +
                        "sum(Visibility) as Visibility, " +
                        "Rival, " +
                        "FROM\n")
                .appendTable(imprt.getSourceTable()).appendText("\n")
                .appendText("GROUP BY  Domain, GroupId, Rival, RivalType")
                .appendText("UNION ALL\n")
                .appendText("SELECT Domain, " +
                        "GroupId, " +
                        "RivalType, " +
                        "RegionId, " +
                        "sum(Visibility) as Visibility, " +
                        "Rival, " +
                        "FROM\n")
                .appendTable(imprt.getSourceTable()).appendText("\n")
                .appendText("GROUP BY  Domain, GroupId, Rival, RivalType, RegionId")
                .appendText(")) \n GROUP BY ShardId, RowId;")
                .appendText("COMMIT;\n\n");
    }

    @Override
    protected YtClickhouseDataLoad rename(YtClickhouseDataLoad imprt) throws Exception {
        log.info("CREATING QUERY: {}", ImportNicheRivalsReportTask.TABLE.createReplicatedMergeTree(-1, ""));
        String database = getTable().getDatabase();
        String tempTableName = getTable().replicatedMergeTreeTableName(-1, imprt.getData());
        // удаляем из общей таблицы подливаемую дату и вливаем свежие данные
        for (ClickhouseHost host : clickhouseServer.getHosts()) {
            boolean hasTable = !clickhouseSystemTablesCHDao.getTables(host, database,
                    Collections.singleton(tempTableName)).isEmpty();
            if (!hasTable) {
                continue;
            }
            // получим минимальный и максимальный ts в новой таблице
            ClickhouseQueryContext.Builder ctx =
                    ClickhouseQueryContext.useDefaults().setHost(host).setTimeout(Duration.standardMinutes(10L));
            StrSubstitutor substitutor = new StrSubstitutor(Map.of(
                    "DB", database,
                    "FULL_TABLE", TABLE_NAME,
                    "TEMP_TABLE", tempTableName,
                    "DATE", imprt.getDateFrom().toString(),
                    "MONTH", PARTITION_MONTH_FORMAT.print(imprt.getDateFrom()),
                    "YEAR", String.valueOf(imprt.getDateFrom().getYear())
            ));
            for (String query : substitutor.replace(MERGE_TABLE_QUERY).split("\n\n")) {
                clickhouseServer.execute(ctx, query);
            }
        }

        return imprt.withNextState();
    }

    protected YtClickhouseDataLoad replicate(YtClickhouseDataLoad imprt) throws Exception {
        // no replication needed
        return imprt.withNextState();
    }

    @Override
    protected CHTable getTable() {
        return TABLE;
    }

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

    @Override
    protected YtClickhouseDataLoadType getImportType() {
        return YtClickhouseDataLoadType.NICHE2_RIVAL_REPORT;
    }

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


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