package ru.yandex.webmaster3.worker.achievements;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;

import ru.yandex.webmaster3.storage.achievements.dao.AchievementsCHDao.F;
import ru.yandex.webmaster3.storage.clickhouse.TableType;
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.CHType;
import ru.yandex.webmaster3.storage.util.yt.YtException;
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;

/**
 * Created by Oleg Bazdyrev on 16/01/2019.
 */
public class ImportAchievementsTask extends AbstractYqlPrepareImportTask {

    private static final int LINES_COUNT = 256;
    protected static final Map<CHType, String> DEFAULT_VALUES = ImmutableMap.of(
            CHPrimitiveType.String, "''",
            CHPrimitiveType.UInt8, "'0'"
    );

    private static final CHTable TABLE = CHTable.builder()
            .database(AbstractClickhouseDao.DB_WEBMASTER3)
            .name("achievements_%s")
            .partitionBy("toYYYYMM(date)")
            .keyField("date", CHPrimitiveType.Date)
            .keyField(F.OWNER, CHPrimitiveType.String)
            .field(F.USER_SELECTION, CHPrimitiveType.UInt8)
            .field(F.POPULAR, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_AUTO, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_MFO, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_SSD, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_GOV, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_YANDEX, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_CBR, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_CBRF_BKI, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_CBR_LICENSE_OFF, CHPrimitiveType.UInt8)
            .field(F.SERVICE_CENTER, CHPrimitiveType.String)
            .field(F.BRANDS, CHPrimitiveType.String)
            .field(F.UNSAFE_HOST, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_AIRLINE, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_EMBASSY, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_VISA_CENTER, CHPrimitiveType.UInt8)
            .field(F.HTTPS, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_CBRF_PROF, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_CBRF_STOCK, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_CBRF_KOPURCB, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_CBRF_NPF, CHPrimitiveType.UInt8)
            .field(F.OFFICIAL_THEATRE, CHPrimitiveType.UInt8)
            .field(F.SPEED_GRADE_MOBILE,CHPrimitiveType.UInt8)
            .field(F.TURBO_WM,CHPrimitiveType.UInt8)
            .field(F.DOCS_ON_SEARCH,CHPrimitiveType.Int64)
            .field(F.IKS,CHPrimitiveType.Int32)
            .field(F.VIDEOHOST_RATING, CHPrimitiveType.Int64)
            .sharded(false)
            .build();

    private static final Map<String, String> SOURCE_EXPRESSIONS = new ImmutableMap.Builder<String, String>()
            .put(F.OWNER, "MascotOwner")
            .put(F.USER_SELECTION, "cast(nvl(UserSelection, 0) as String)")
            .put(F.POPULAR, "cast(nvl(Popular, 0) as String)")
            .put(F.OFFICIAL_AUTO, "if(OfficialAuto is not null, '1', '0')")
            .put(F.OFFICIAL_MFO, "if(OfficialMfo, '1', '0')")
            .put(F.OFFICIAL_SSD, "if(OfficialSsd, '1', '0')")
            .put(F.OFFICIAL_GOV, "if(Official, '1', '0')")
            .put(F.OFFICIAL_YANDEX, "if(OfficialYandex, '1', '0')")
            .put(F.OFFICIAL_CBR, "if(OfficialCbr, '1', '0')")
            .put(F.OFFICIAL_CBRF_BKI, "if(OfficialCbrfBki, '1', '0')")
            .put(F.OFFICIAL_CBR_LICENSE_OFF, "if(OfficialCbrRecalled, '1', '0')")
            .put(F.SERVICE_CENTER, "String::EscapeC(nvl(ServiceCenter, ''))")
            .put(F.BRANDS, "String::EscapeC(nvl(Brands, ''))")
            .put(F.UNSAFE_HOST, "'0'")
            .put(F.OFFICIAL_AIRLINE, "if(OfficialAvia, '1', '0')")
            .put(F.OFFICIAL_EMBASSY, "if(OfficialEmbassy, '1', '0')")
            .put(F.OFFICIAL_VISA_CENTER, "if(OfficialVisaCenter, '1', '0')")
            .put(F.HTTPS, "if(Https == true, '1', if (Https == false, '2', '0'))")
            .put(F.OFFICIAL_CBRF_PROF, "if(OfficialCbrfProf, '1', '0')")
            .put(F.OFFICIAL_CBRF_STOCK, "if(OfficialCbrfStock, '1', '0')")
            .put(F.OFFICIAL_CBRF_KOPURCB, "if(OfficialCbrfKopurcb, '1', '0')")
            .put(F.OFFICIAL_CBRF_NPF, "if(OfficialCbrfNpf, '1', '0')")
            .put(F.OFFICIAL_THEATRE, "if(OfficialTheatre, '1', '0')")
            .put(F.SPEED_GRADE_MOBILE,"cast(nvl(SpeedGradeMobile, 0) as String)")
            .put(F.TURBO_WM,"if(TurboWm, '1', '0')")
            .put(F.DOCS_ON_SEARCH, "cast(nvl(WMDocsOnSearch, 0) as String)")
            .put(F.IKS, "cast(nvl(Iks, 0) as String)")
            .put(F.VIDEOHOST_RATING, "cast(nvl(VideohostRating, -1) as String)")
            .build();

    private Set<String> disabledFields = new HashSet<>();

    protected Map<String, String> getSourceExpressions() {
        return SOURCE_EXPRESSIONS;
    }

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

    @Override
    protected YqlQueryBuilder prepareIntermediateTable(YtClickhouseDataLoad imprt) {
        String dateString = IN_YQL_QUERY_DATE_FORMATTER.print(imprt.getDateTo());
        int shardCount = getShardsCount();
        String fields = getTable().getFields().stream().filter(chField -> !chField.getName().equals("date"))
                .map(chField -> {
                    String result;
                    if (disabledFields.contains(chField.getName())) {
                        result = DEFAULT_VALUES.get(chField.getType());
                    } else {
                        result = getSourceExpressions().get(chField.getName());
                    }
                    Preconditions.checkState(result != null);
                    return result;
                }).collect(Collectors.joining(" || '\\t' || ", "('" + dateString + "'|| '\\t' || ", " || '\\n')"));


        return YqlQueryBuilder.newBuilder()
                .cluster(tablePath)
                .appendText("PRAGMA yt.MaxRowWeight = '128M';\n")
                .appendText("INSERT INTO " + INTERMEDIATE_TABLE)
                .appendText("SELECT ShardId, RowId, Compress::Gzip(String::JoinFromList(AGGREGATE_LIST(data), ''), 6) as data FROM (\n")
                .appendText("SELECT")
                .appendText("(Digest::Fnv64(MascotOwner) % " + shardCount + ") as ShardId,")
                .appendText("((Digest::Fnv64(MascotOwner) / " + shardCount + ") % " + LINES_COUNT + ") as RowId,")
                .appendText(fields).appendText("as data ")
                .appendText("FROM")
                .appendTable(tablePath)
                .appendText(") \n GROUP BY ShardId, RowId;")
                .appendText("COMMIT;\n\n");
    }

    @Override
    protected YtClickhouseDataLoad init(YtClickhouseDataLoad latestImport) throws InterruptedException, YtException {
        return initByUpdateDate(latestImport);
    }

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

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

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

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

    public void setDisabledFields(String s) {
        if (!Strings.isNullOrEmpty(s)) {
            disabledFields = new HashSet<>(Arrays.asList(s.split(",")));
        }
    }
}
