package ru.yandex.webmaster3.worker.siteservices;

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 org.springframework.beans.factory.annotation.Required;

import ru.yandex.webmaster3.storage.clickhouse.TableType;
import ru.yandex.webmaster3.storage.services.SiteServicesCHDao.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.CHType;
import ru.yandex.webmaster3.storage.util.yt.YtException;
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;

/**
 * Created by shabashoff on 16/01/2019.
 */
public class ImportSiteServiceTask 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("siteservices_%s")
            .partitionBy("toYYYYMM(date)")
            .sharded(false)
            .keyField("date", CHPrimitiveType.Date)
            .keyField(F.OWNER, CHPrimitiveType.String)
            .field(F.ALICE_INFO,CHPrimitiveType.String)
            .field(F.COMPANY_INFO,CHPrimitiveType.String)
            .field(F.MOBILE_APP_INFO,CHPrimitiveType.String)
            .field(F.IS_SHOP,CHPrimitiveType.UInt8)
            .field(F.MARKET_RATING,CHPrimitiveType.Float32)
            .field(F.MARKET_ID,CHPrimitiveType.UInt64)
            .field(F.USE_DIRECT,CHPrimitiveType.UInt8)
            .field(F.USE_METRIKA,CHPrimitiveType.UInt8)
            .field(F.RADAR_TYPE,CHPrimitiveType.String)
            .field(F.SNIPPET_PIC, CHPrimitiveType.UInt8)
            .field(F.IS_NEWS, CHPrimitiveType.UInt8)
            .field(F.VERTICAL_SHARE_RATING, CHPrimitiveType.Float32)
            .field(F.VERTICAL_SHARE_IS_VALIDATED, CHPrimitiveType.UInt8)
            .partsToThrowInsert(1024)
            .build();

    private static final Map<String, String> SOURCE_EXPRESSIONS = new ImmutableMap.Builder<String, String>()
            .put(F.OWNER, "$escape(MascotOwner)")
            .put(F.ALICE_INFO, "$escape(nvl(AliceInfo, ''))")
            .put(F.MOBILE_APP_INFO, "$escape(nvl(MobileAppInfo, ''))")
            .put(F.COMPANY_INFO, "$escape(nvl(CompanyInfo, ''))")
            .put(F.IS_SHOP, "if(IsShop, '1', '0')")
            .put(F.MARKET_RATING,"cast(nvl(MarketRating, 0) as String)")
            .put(F.MARKET_ID,"cast(nvl(MarketId, 0) as String)")
            .put(F.USE_DIRECT,"if(UseDirect, '1', '0')")
            .put(F.USE_METRIKA,"if(UseMetrika, '1', '0')")
            .put(F.RADAR_TYPE,"$escape(nvl(RadarType, ''))")
            .put(F.SNIPPET_PIC, "if(HaveSnippetPic,'1', '0')")
            .put(F.IS_NEWS, "if(IsNews,'1', '0')")
            .put(F.VERTICAL_SHARE_RATING, "cast(nvl(VerticalShareRating, 0) as String)")
            .put(F.VERTICAL_SHARE_IS_VALIDATED, "if(VerticalShareIsValidated, '1', '0')")
            .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')"));


        YqlQueryBuilder yqlQueryBuilder = YqlQueryBuilder.newBuilder()
                .cluster(tablePath)
                .appendFDefinition(YqlFunctions.ESCAPE)
                .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");

        return yqlQueryBuilder;
    }

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

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

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

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

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

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