package ru.yandex.webmaster3.worker.mirrors;

import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import com.datastax.driver.core.utils.UUIDs;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import ru.yandex.webmaster3.storage.clickhouse.ClickhouseTableInfo;
import ru.yandex.webmaster3.storage.clickhouse.TableProvider;
import ru.yandex.webmaster3.storage.clickhouse.TableSource;
import ru.yandex.webmaster3.storage.clickhouse.TableState;
import ru.yandex.webmaster3.storage.clickhouse.TableType;
import ru.yandex.webmaster3.storage.clickhouse.dao.ClickhouseTablesRepository;
import ru.yandex.webmaster3.storage.mirrors.dao.MainMirrorsCHDao;
import ru.yandex.webmaster3.storage.mirrors.dao.NewMainMirrorCacheYDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHTable;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseQueryContext;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseServer;
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.ytimport.AbstractYtClickhouseDataLoadTask;

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

@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class ImportMirrors2Task extends AbstractYtClickhouseDataLoadTask {

    @Qualifier("legacyMdbClickhouseServer")
    private final ClickhouseServer clickhouseServer;
    @Qualifier("legacyClickhouseTablesYDao")
    private final ClickhouseTablesRepository legacyClickhouseTablesYDao;
    @Qualifier("legacyMdbTableStorage")
    private final TableProvider tableStorage;
    private final NewMainMirrorCacheYDao newMainMirrorCacheYDao;

    @Override
    protected YtClickhouseDataLoad init(YtClickhouseDataLoad latestImport) throws Exception {
        String[] splitData = Optional.ofNullable(latestImport.getData()).orElse("0 0").split(" ");
        String prevHostsTable = splitData[0];
        String prevAllMirrorsTable = splitData[1];
        String hostsTable = tableStorage.getTable(TableType.HOSTS).getLocalTableName();
        String allMirrorsTable = tableStorage.getTable(TableType.ALL_MIRRORS).getLocalTableName();
        if (prevAllMirrorsTable.equals(allMirrorsTable) && prevHostsTable.equals(hostsTable)) {
            return latestImport.withState(YtClickhouseDataLoadState.DONE);
        }

        LocalDate dateFrom = DateTime.now().toLocalDate();


        //  нужно прокидывать hostsTable, allMirrorsTable, versionForWmcMirrors
        return latestImport.withData(hostsTable + " " + allMirrorsTable + " " + DateTime.now().getMillis())
                .withSourceTable(tablePath, dateFrom, dateFrom)
                .withState(YtClickhouseDataLoadState.IMPORTING);
    }

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

    @Override
    protected YtClickhouseDataLoad doImport(YtClickhouseDataLoad imprt) throws Exception {
        String[] splitData = imprt.getData().split(" ");
        String hostsVersion = splitData[0];
        String allMirrorsVersion = splitData[1];
        String version = splitData[2];
        String creatSpec = getTable().createReplicatedMergeTreeSpec(-1, version);
        String createQuery = String.format("CREATE TABLE %1$s.%2$s %3$s", getTable().getDatabase(),
                getTable().replicatedMergeTreeTableName(-1, version),
                creatSpec);

        String dropQuery = String.format("DROP TABLE IF EXISTS %1$s.%2$s", getTable().getDatabase(),
                getTable().replicatedMergeTreeTableName(-1, version));

        clickhouseServer.executeOnAllHosts(dropQuery);

        ClickhouseQueryContext.Builder chQCtx = ClickhouseQueryContext.useDefaults()
                .setHost(clickhouseServer.pickAliveHostOrFail(0));
        chQCtx.setTimeout(Duration.standardMinutes(6));

        final Map<String, String> values = Map.of(
                "TABLE_NAME", getTable().getDatabase() + "." + getTable().replicatedMergeTreeTableName(-1, version),
                "ALL_MIRRORS", allMirrorsVersion,
                "WMC_HOSTS", hostsVersion
        );
        StrSubstitutor sub = new StrSubstitutor(values);

        String insertTemplate = """
                INSERT INTO ${TABLE_NAME}(date, host, main_host)
                SELECT all_mirrors.date, all_mirrors.host, all_mirrors.main_host FROM\s
                ${ALL_MIRRORS} AS all_mirrors
                INNER JOIN\s
                ${WMC_HOSTS} AS wmc_hosts
                ON all_mirrors.host=wmc_hosts.host_id
                """;

        String insertQuery = sub.replace(insertTemplate);


        log.info("CREATE START - {}", DateTime.now());
        clickhouseServer.execute(chQCtx, createQuery);
        log.info("INSERT START - {}", DateTime.now());
        clickhouseServer.execute(chQCtx, insertQuery);
        log.info("INSERT FINISH - {}", DateTime.now());

        return imprt.withNextState();
    }

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

    @Override
    protected YtClickhouseDataLoad replicate(YtClickhouseDataLoad imprt) throws Exception {
        String[] splitData = imprt.getData().split(" ");
        var command=  clickhouseReplicationManager.nonShardedReplication(
                getTable().getDatabase(),
                getTable().replicatedMergeTreeTableName(-1, splitData[2]),
                getTable().createReplicatedMergeTreeSpec(-1, splitData[2])
        );
        clickhouseReplicationManager.enqueueReplication(command);
        return imprt.withReplicationTaskIds(command.getReplicationTaskId()).withNextState();
    }

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

    @Override
    protected YtClickhouseDataLoad rename(YtClickhouseDataLoad imprt) throws Exception {
        String[] splitData = imprt.getData().split(" ");

        String chTableName = getTable().getDatabase() + "." + getTable().replicatedMergeTreeTableName(-1,
                splitData[2]);
        legacyClickhouseTablesYDao.update(
                new ClickhouseTableInfo(getTableType(), UUIDs.timeBased(),
                        TableState.ON_LINE, DateTime.now(), TableSource.YT_HAHN, null,
                        chTableName, chTableName, chTableName, 0, getTable().getParts())
        );
        newMainMirrorCacheYDao.clear();
        return imprt.withNextState();
    }

    public TableType getTableType() {
        return TableType.MIRRORS2;
    }

    public CHTable getTable() {
        return MainMirrorsCHDao.TABLE_V2;
    }

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

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