package ru.yandex.webmaster3.storage.importer.dao;

import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import org.joda.time.DateTime;
import org.springframework.stereotype.Repository;

import ru.yandex.webmaster3.core.util.enums.EnumResolver;
import ru.yandex.webmaster3.core.util.json.JsonMapping;
import ru.yandex.webmaster3.storage.importer.model.ImportDefinition;
import ru.yandex.webmaster3.storage.importer.model.ImportStage;
import ru.yandex.webmaster3.storage.importer.model.ImportTask;
import ru.yandex.webmaster3.storage.util.ydb.AbstractYDao;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.DataMapper;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Field;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Fields;
import ru.yandex.webmaster3.storage.util.yt.YtPath;

/**
 * Created by Oleg Bazdyrev on 24/09/2020.
 */
@Repository
public class ClickhouseImportTasksYDao extends AbstractYDao {

    private static final String TABLE_NAME = "clickhouse_import_tasks";
    private static final TypeReference<Map<ImportStage, JsonNode>> DATA_MAP_REFERENCE = new TypeReference<>() {
    };

    private static final DataMapper<ImportTask> MAPPER = DataMapper.create(
            F.ID, F.ENABLED, F.STAGE, F.SOURCE_TABLE, F.INTERMEDIATE_TABLE, F.DATA, F.PREV_DATA, F.DATABASE, F.DISTRIBUTED_TABLE_NAME,
            F.LOCAL_TABLE_NAME, F.ALL_TABLE_NAMES, F.STARTED, F.UPDATED, F.FINISHED, F.ERROR, F.ERROR_STAGE, F.DEFINITION, ImportTask::new
    );

    public ClickhouseImportTasksYDao() {
        super(PREFIX_IMPORTER, TABLE_NAME);
    }

    public void update(ImportTask task) {
        var upsert = upsert(
                F.ID.value(task.getId()),
                F.ENABLED.value(task.isEnabled()),
                F.STAGE.value(task.getStage()),
                F.SOURCE_TABLE.value(task.getSourceTable()),
                F.INTERMEDIATE_TABLE.value(task.getIntermediateTable()),
                F.DATA.value(task.getData()),
                F.PREV_DATA.value(task.getPrevData()),
                F.DATABASE.value(task.getDatabase()),
                F.DISTRIBUTED_TABLE_NAME.value(task.getDistributedTableName()),
                F.LOCAL_TABLE_NAME.value(task.getLocalTableName()),
                F.ALL_TABLE_NAMES.value(task.getAllTableNames()),
                F.STARTED.value(task.getStarted()),
                F.UPDATED.value(task.getUpdated()),
                F.FINISHED.value(task.getFinished()),
                F.ERROR.value(task.getError()),
                F.ERROR_STAGE.value(task.getErrorStage()),
                F.DEFINITION.value(task.getDefinition())
        );
        execute(upsert);
    }

    public ImportTask get(String id) {
        return queryOne(select(MAPPER).where(F.ID.eq(id)).getStatement(), MAPPER);
    }

    public List<ImportTask> listAll() {
        return queryForList(select(MAPPER), MAPPER);
    }

    public interface F {
        Field<String> ID = Fields.stringField("id");
        Field<Boolean> ENABLED = Fields.boolField("enabled");
        Field<ImportStage> STAGE = Fields.stringEnumField("stage", EnumResolver.er(ImportStage.class));
        Field<YtPath> SOURCE_TABLE = Fields.ytPathField("source_table").makeOptional();
        Field<YtPath> INTERMEDIATE_TABLE = Fields.ytPathField("intermediate_table").makeOptional();
        Field<Map<ImportStage, JsonNode>> DATA = Fields.jsonField2("data", DATA_MAP_REFERENCE).makeOptional();
        Field<Map<ImportStage, JsonNode>> PREV_DATA = Fields.jsonField2("prev_data", DATA_MAP_REFERENCE).makeOptional();
        Field<String> DATABASE = Fields.stringField("database").makeOptional();
        Field<String> DISTRIBUTED_TABLE_NAME = Fields.stringField("distributed_table_name").makeOptional();
        Field<String> LOCAL_TABLE_NAME = Fields.stringField("local_table_name").makeOptional();
        Field<List<String>> ALL_TABLE_NAMES = Fields.jsonField2("all_table_names", JsonMapping.STRING_LIST_REFERENCE).makeOptional();
        Field<DateTime> STARTED = Fields.jodaDateTimeField("started").makeOptional();
        Field<DateTime> UPDATED = Fields.jodaDateTimeField("updated").makeOptional();
        Field<DateTime> FINISHED = Fields.jodaDateTimeField("finished").makeOptional();
        Field<String> ERROR = Fields.stringField("error").makeOptional();
        Field<ImportStage> ERROR_STAGE = Fields.stringEnumField("error_stage", EnumResolver.er(ImportStage.class)).makeOptional();
        Field<ImportDefinition> DEFINITION = Fields.jsonField2("definition", ImportDefinition.class);
    }
}
