package ru.yandex.webmaster3.worker.metrika;

import java.util.Optional;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import ru.yandex.webmaster3.storage.clickhouse.TableType;
import ru.yandex.webmaster3.storage.metrika.dao.MetrikaStatsByTimeCHDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHTable;
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 ru.yandex.webmaster3.worker.turbo.AbstractYqlPrepareImportTask;

/**
 * @author shabashoff
 */
public class ImportMetrikaStatsByTimeTask extends AbstractYqlPrepareImportTask {
    private static final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd");

    private static final int ROWS_COUNT = 256;

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

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

    @Override
    protected YtClickhouseDataLoad init(YtClickhouseDataLoad lastImport) throws Exception {
        long lastImportTimestamp = lastImport.getData() == null ? 0L : Long.parseLong(lastImport.getData());

        // проверим дату обновления таблички
        return ytService.inTransaction(tablePath).query(cypressService -> {
            Optional<Long> updateTimestampOpt = cypressService.list(tablePath).stream().map(a -> DateTime.parse(a.getName(), FORMATTER).getMillis()).max(Long::compareTo);

            if (updateTimestampOpt.isPresent() && updateTimestampOpt.get() > lastImportTimestamp) {
                Long updateTimestamp = updateTimestampOpt.get();
                LocalDate dateFrom = new DateTime(updateTimestamp).toLocalDate();
                return lastImport.withData(String.valueOf(updateTimestamp))
                        .withSourceTable(tablePath, dateFrom, dateFrom);
            }

            // ничего нового
            return lastImport.withState(YtClickhouseDataLoadState.DONE);
        });
    }

    @Override
    protected YqlQueryBuilder prepareIntermediateTable(YtClickhouseDataLoad imprt) {
        int shardCount = getShardsCount();

        return YqlQueryBuilder.newBuilder()
                .cluster(tablePath)
                .appendText("PRAGMA yt.MaxRowWeight = '128M';")
                .appendText("PRAGMA yt.ForceInferSchema;")
                .appendText("PRAGMA yt.DefaultMemoryLimit = '4G';")
                .appendText("use arnold;")
                .appendText("$names=(")
                .appendText("SELECT " +
                            "    MIN(name) as start," +
                            "    MAX(name) as stop " +
                            "    FROM (" +
                            "       SELECT TableName(Path, \"yt\") as name " +
                            "       FROM FOLDER(`"+tablePath.getPathWithoutCluster()+"`) " +
                            "       ORDER BY name DESC LIMIT 14));" +
                            "$start=(SELECT start FROM $names);" +
                            "$stop=(SELECT stop FROM $names);" +
                            "$rows = SELECT String::EscapeC(Host) as Host,CounterID,Visits,BounceCount,IsMobile,IsPad,TableName() as table_name " +
                            " FROM RANGE (`"+tablePath.getPathWithoutCluster()+"`, $start, $stop);")
                .appendText("")
                .appendText("INSERT INTO " + INTERMEDIATE_TABLE)
                .appendText(" SELECT ShardId, RowId, Compress::Gzip(String::JoinFromList(AGGREGATE_LIST(data), ''), 6) as data FROM ")
                .appendText("(SELECT ")
                .appendText("(Digest::Fnv64(CAST(CounterID as String)) % " + shardCount + ") as ShardId,")
                .appendText("((Digest::Fnv64(CAST(CounterID as String) || Host) / " + shardCount + ") % " + ROWS_COUNT + ") as RowId,")
                .appendText("(")
                .appendText("$stop ")
                .appendText("|| '\\t' || CAST(CounterID as String) ")
                .appendText("|| '\\t' || Url::CutWWW(Url::HostNameToPunycode(String::ToLower(Url::GetHost(Host)))) ")
                .appendText("|| '\\t' || CAST(table_name as String) ")
                .appendText("|| '\\t' || CAST(COALESCE(Visits,0) as String) ")
                .appendText("|| '\\t' || CAST(COALESCE(BounceCount,0) as String) ")
                .appendText("|| '\\n') as data ")
                .appendText("FROM $rows ")
                .appendText(" WHERE IsMobile = false and IsPad = false) ")
                .appendText(" GROUP BY ShardId, RowId;")
                .appendText(" COMMIT;");
    }

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

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