package ru.yandex.webmaster3.storage.url.checker2.dao;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.joda.time.DateTime;
import org.springframework.stereotype.Repository;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.storage.url.checker2.data.UrlCheckInfo;
import ru.yandex.webmaster3.storage.url.checker2.data.UrlCheckResultData;
import ru.yandex.webmaster3.storage.url.common.dao.CommonUrlCheckRequestsYDao;
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 java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * @author leonidrom
 */
@Repository
public class UrlCheckResultsYDao extends AbstractYDao {
    private static final ObjectMapper OM = new ObjectMapper()
            .registerModules(new JodaModule(), new ParameterNamesModule());

    public UrlCheckResultsYDao() {
        super("/webmaster3/urlcheck", "url_check_results");
    }

    public List<UrlCheckResultData> getResults(WebmasterHostId hostId) {
        var st = select(MAPPER)
                .where(F.HOST_ID.eq(hostId))
                .order(F.REQUEST_ID.asc());

        return queryForList(st, MAPPER, list -> {
            var maxReq = list.get(list.size() - 1);
            var cond = F.REQUEST_ID.gt(maxReq.getRequestId());

            return st.cont(cond).getStatement();
        });

    }

    public UrlCheckResultData getResult(WebmasterHostId hostId, UUID requestId) {
        return select(MAPPER)
                .where(F.HOST_ID.eq(hostId))
                .and(F.REQUEST_ID.eq(requestId))
                .queryOne();
    }

    public void storeResult(WebmasterHostId hostId, UUID requestId, DateTime completedTime, UrlCheckInfo urlInfo) {
        upsert(
                F.HOST_ID.value(hostId),
                F.REQUEST_ID.value(requestId),
                F.COMPLETED_TIME.value(completedTime),
                F.URL_INFO.value(serializeUrlInfo(urlInfo))
        ).execute();
    }

    public void deleteResult(WebmasterHostId hostId, UUID requestId) {
        delete()
                .where(F.HOST_ID.eq(hostId))
                .and(F.REQUEST_ID.eq(requestId))
                .execute();
    }

    private static String serializeUrlInfo(UrlCheckInfo info) {
        try {
            return OM.writeValueAsString(info);
        } catch (IOException e) {
            throw new WebmasterException("Failed to serialize url check info to DB",
                    new WebmasterErrorResponse.InternalUnknownErrorResponse(UrlCheckResultsYDao.class, null),
                    e);
        }
    }

    private static UrlCheckInfo deserializeUrlInfo(String info) {
        try {
            return OM.readValue(info, UrlCheckInfo.class);
        } catch (IOException e) {
            throw new WebmasterException("Failed to deserialize " + info + "url check result from DB",
                    new WebmasterErrorResponse.InternalUnknownErrorResponse(UrlCheckResultsYDao.class, null), e);
        }
    }

    private static final class F {
        static final Field<WebmasterHostId> HOST_ID = Fields.hostIdField("host_id");
        static final Field<UUID> REQUEST_ID = Fields.uuidField("request_id");
        static final Field<DateTime> COMPLETED_TIME = Fields.jodaDateTimeField("completed_time");
        static final Field<String> URL_INFO = Fields.stringField("url_info");
    }

    private static final DataMapper<UrlCheckResultData> MAPPER = DataMapper.create(
            F.HOST_ID, F.REQUEST_ID, F.COMPLETED_TIME, F.URL_INFO,
            (hostId, requestId, completedTme, urlInfoString) -> {
                UrlCheckInfo urlInfo = deserializeUrlInfo(urlInfoString);
                return new UrlCheckResultData(hostId, requestId, completedTme, urlInfo);
            }
    );

}
