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

import java.util.List;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import lombok.Setter;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.Nullable;
import org.joda.time.DateTime;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.sitestructure.RawSearchUrlStatusEnum;
import ru.yandex.webmaster3.core.sitestructure.SearchUrlStatusEnum;
import ru.yandex.webmaster3.core.sitestructure.SearchUrlStatusUtil;
import ru.yandex.webmaster3.storage.clickhouse.ClickhouseTableInfo;
import ru.yandex.webmaster3.storage.clickhouse.TableProvider;
import ru.yandex.webmaster3.storage.clickhouse.TableType;
import ru.yandex.webmaster3.storage.importanturls.data.ImportantUrl;
import ru.yandex.webmaster3.storage.importanturls.data.ImportantUrlStatus;
import ru.yandex.webmaster3.storage.searchurl.SearchUrlSamplesService;
import ru.yandex.webmaster3.storage.searchurl.samples.data.UrlStatusInfo;
import ru.yandex.webmaster3.storage.util.clickhouse2.AbstractClickhouseDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHField;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHPrimitiveType;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHRow;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHTable;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.OrderBy;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.QueryBuilder;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.Statement;
import ru.yandex.webmaster3.core.util.http.YandexMimeType;

/**
 * Created by Oleg Bazdyrev on 23/05/2019.
 */
public class ImportantUrlsHistoryCHDao extends AbstractClickhouseDao {

    @Setter
    private TableProvider tableStorage;

    private static ImportantUrlStatus statusFromRow(CHRow row) {
        String title = SearchUrlSamplesService.hideDownloadEvidence(
                RawSearchUrlStatusEnum.R.fromValue(row.getInt(F.URL_STATUS)), row.getString(F.TITLE));
        ImportantUrlStatus.IndexingInfo indexingInfo = indexingInfoFromRow(row);
        ImportantUrlStatus.SearchInfo searchInfo = searchInfoFromRow(row);
        return new ImportantUrlStatus(
                getDateTimeFromUnixTs(row, F.UPDATE_TIME),
                title,
                row.getInt(F.TITLE_CHANGED) == 1,
                row.getInt(F.SPREAD_HTTP_CODE_CHANGED) == 1,
                row.getInt(F.URL_STATUS_CHANGED) == 1,
                row.getInt(F.DESCRIPTION_CHANGED) == 1,
                row.getInt(F.REL_CANONICAL_TARGET_CHANGED) == 1,
                indexingInfo,
                searchInfo
        );
    }

    @Nullable
    private static ImportantUrlStatus.IndexingInfo indexingInfoFromRow(CHRow row) {
        DateTime lastAccess = getDateTimeFromUnixTs(row, F.SPREAD_LAST_ACCESS);
        int httpCode = row.getInt(F.SPREAD_HTTP_CODE);
        int mimeType = row.getInt(F.SPREAD_MIME_TYPE);

        if (lastAccess == null) {
            return null;
        }
        return new ImportantUrlStatus.IndexingInfo(httpCode, lastAccess, YandexMimeType.R.fromValue(mimeType));
    }

    @Nullable
    private static ImportantUrlStatus.SearchInfo searchInfoFromRow(CHRow row) {
        RawSearchUrlStatusEnum rawStatus = RawSearchUrlStatusEnum.R.fromValue(row.getInt(F.URL_STATUS));
        if (rawStatus == RawSearchUrlStatusEnum.NOTHING_FOUND) {
            return null;
        }
        int httpCode = row.getInt(F.HTTP_CODE);
        DateTime lastAccess = getDateTimeFromUnixTs(row, F.LAST_ACCESS);
        UrlStatusInfo statusInfo;
        // информация о статусе поиска
        SearchUrlStatusEnum status = SearchUrlStatusUtil.raw2View(rawStatus, row.getInt(F.IS_SEARCHABLE) == 1);

        statusInfo = new UrlStatusInfo(status,
                getDateTimeFromUnixTs(row, F.ADD_TIME),
                row.getString(F.BEAUTY_URL),
                httpCode,
                row.getString(F.MAIN_HOST),
                row.getString(F.MAIN_PATH),
                row.getString(F.REDIR_TARGET),
                row.getString(F.REL_CANONICAL_TARGET),
                row.getString(F.DESCRIPTION),
                false,
                false,
                false,
                false,
                false
        );
        YandexMimeType mimeType = YandexMimeType.R.fromValueOrUnknown(row.getInt(F.MIME_TYPE));
        return new ImportantUrlStatus.SearchInfo(httpCode, lastAccess, mimeType, statusInfo);
    }

    public List<ImportantUrlStatus> getHistory(WebmasterHostId hostId, String path) {
        ClickhouseTableInfo table = tableStorage.getTable(TableType.IMPORTANT_URLS_HISTORY);
        Statement st = QueryBuilder.select(FIELDS).from(table.getLocalTableName())
                .where(QueryBuilder.eq(F.HOST_ID, hostId))
                .and(QueryBuilder.eq(F.PATH, path))
                .orderBy(Lists.newArrayList(
                        //Pair.of(F.MAX_LAST_ACCESS, OrderBy.Direction.DESC),
                        Pair.of(F.UPDATE_TIME, OrderBy.Direction.DESC))
                );

        return getClickhouseServer().queryAll(table.chContext(getClickhouseServer(), hostId), st.toString(),
                ImportantUrlsHistoryCHDao::statusFromRow);
    }

    public List<ImportantUrl> getLastStates(WebmasterHostId hostId) {
        ClickhouseTableInfo table = tableStorage.getTable(TableType.IMPORTANT_URLS_LAST_STATE);
        Statement st = QueryBuilder.select(FIELDS).from(table.getLocalTableName())
                .where(QueryBuilder.eq(F.HOST_ID, hostId));

        return getClickhouseServer().queryAll(table.chContext(getClickhouseServer(), hostId), st.toString(), row -> {
            ImportantUrlStatus lastStatus = statusFromRow(row);
            return new ImportantUrl(
                    hostId,
                    row.getString(F.PATH),
                    getDateTimeFromUnixTs(row, F.ADD_TIME),
                    lastStatus,
                    lastStatus.isHaveChange()
            );
        });
    }

    @Nullable
    private static DateTime getDateTimeFromUnixTs(CHRow row, String field) {
        long seconds = row.getLongUnsafe(field);
        if (seconds == 0L) {
            return null;
        }
        return new DateTime(seconds * 1000L);
    }

    public interface F {
        String DATE = "date";
        String HOST_ID = "host_id";
        String PATH = "path";
        String UPDATE_TIME = "update_time";
        String URL_STATUS = "url_status";
        String BEAUTY_URL = "beauty_url";
        String LAST_ACCESS = "last_access";
        String ADD_TIME = "add_time";
        String MAIN_HOST = "main_host";
        String MAIN_PATH = "main_path";
        String MAIN_MIRROR_HOST = "main_mirror_host";
        String REDIR_TARGET = "redir_target";
        String HTTP_CODE = "http_code";
        String MIME_TYPE = "mime_type";
        String REL_CANONICAL_TARGET = "rel_canonical_target";
        String IS_INDEXED = "is_indexed";
        String IS_FAKE = "is_fake";
        String IS_SEARCHABLE = "is_searchable";
        String IS_TURBO = "is_turbo";
        String JUPITER_TIMESTAMP = "jupiter_timestamp";
        String SPREAD_LAST_ACCESS = "spread_last_access";
        String SPREAD_HTTP_CODE = "spread_http_code";
        String SPREAD_MIME_TYPE = "spread_mime_type";
        String TITLE = "title";
        String DESCRIPTION = "description";
        String SPREAD_HTTP_CODE_CHANGED = "spread_http_code_changed";
        String URL_STATUS_CHANGED = "url_status_changed";
        String TITLE_CHANGED = "title_changed";
        String DESCRIPTION_CHANGED = "description_changed";
        String REL_CANONICAL_TARGET_CHANGED = "rel_canonical_target_changed";
    }

    public static final CHTable.Builder TABLE_BUILDER = CHTable.builder()
            .database(AbstractClickhouseDao.DB_WEBMASTER3_IMPORTANTURLS)
            .name("history_%s")
            .sharded(true)
            .parts(1)
            .partitionBy("toYYYYMM(" + F.DATE + ")")
            .keyField(F.DATE, CHPrimitiveType.Date)
            .keyField(F.HOST_ID, CHPrimitiveType.String)
            .keyField(F.PATH, CHPrimitiveType.String)
            .keyField(F.UPDATE_TIME, CHPrimitiveType.UInt64)
            .field(F.URL_STATUS, CHPrimitiveType.Int32)
            .field(F.BEAUTY_URL, CHPrimitiveType.String)
            .field(F.LAST_ACCESS, CHPrimitiveType.UInt64)
            .field(F.ADD_TIME, CHPrimitiveType.UInt64)
            .field(F.MAIN_HOST, CHPrimitiveType.String)
            .field(F.MAIN_PATH, CHPrimitiveType.String)
            .field(F.MAIN_MIRROR_HOST, CHPrimitiveType.String)
            .field(F.REDIR_TARGET, CHPrimitiveType.String)
            .field(F.HTTP_CODE, CHPrimitiveType.Int32)
            .field(F.MIME_TYPE, CHPrimitiveType.Int32)
            .field(F.REL_CANONICAL_TARGET, CHPrimitiveType.String)
            .field(F.IS_INDEXED, CHPrimitiveType.UInt8)
            .field(F.IS_FAKE, CHPrimitiveType.UInt8)
            .field(F.IS_SEARCHABLE, CHPrimitiveType.UInt8)
            //.field(F.IS_TURBO, CHPrimitiveType.UInt8)
            .field(F.JUPITER_TIMESTAMP, CHPrimitiveType.UInt64)
            .field(F.SPREAD_LAST_ACCESS, CHPrimitiveType.UInt64)
            .field(F.SPREAD_HTTP_CODE, CHPrimitiveType.Int32)
            .field(F.SPREAD_MIME_TYPE, CHPrimitiveType.Int32)
            .field(F.TITLE, CHPrimitiveType.String)
            .field(F.DESCRIPTION, CHPrimitiveType.String)
            .field(F.SPREAD_HTTP_CODE_CHANGED, CHPrimitiveType.UInt8)
            .field(F.URL_STATUS_CHANGED, CHPrimitiveType.UInt8)
            .field(F.TITLE_CHANGED, CHPrimitiveType.UInt8)
            .field(F.DESCRIPTION_CHANGED, CHPrimitiveType.UInt8)
            .field(F.REL_CANONICAL_TARGET_CHANGED, CHPrimitiveType.UInt8);

    public static final CHTable HISTORY_TABLE = TABLE_BUILDER.name("history_%s").build();
    public static final CHTable LAST_STATE_TABLE = TABLE_BUILDER.name("last_state_%s").build();

    private static final List<String> FIELDS = HISTORY_TABLE.getFields().stream()
            .map(CHField::getName).collect(Collectors.toList());

}
