package ru.yandex.webmaster3.coordinator.http;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.Action;
import ru.yandex.webmaster3.core.http.ActionRequest;
import ru.yandex.webmaster3.core.http.ActionResponse;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.url.checker3.UrlCheckRequestParams;
import ru.yandex.webmaster3.core.util.json.JsonMapping;
import ru.yandex.webmaster3.storage.url.checker3.dao.UrlCheckDataBlocksYDao;
import ru.yandex.webmaster3.storage.url.checker3.data.UrlCheckDataBlock;
import ru.yandex.webmaster3.storage.url.checker3.data.UrlCheckDataBlockState;
import ru.yandex.webmaster3.storage.url.checker3.data.UrlCheckDataBlockType;
import ru.yandex.webmaster3.storage.url.checker3.data.blocks.UrlCheckRequestParamsData;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.util.yt.YtService;
import ru.yandex.webmaster3.storage.util.yt.YtTransactionService;
import ru.yandex.webmaster3.storage.util.yt.YtUtils;

import java.util.*;

/**
 * @author leonidrom
 */
@Component("/tmp/uploadFailedBlocks")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UploadFailedUrlCheckBlocks extends Action<UploadFailedUrlCheckBlocks.Req, UploadFailedUrlCheckBlocks.Resp> {
    @Value("${external.yt.service.arnold.root.default}/export/failed_checkurl_blocks")
    private final YtPath ytTablePath;
    private final YtService ytService;
    private final UrlCheckDataBlocksYDao urlCheckDataBlocksYDao;

    private static final String TABLE_SCHEMA = "[" +
            "{'name': '" + YtRow.F_ADD_DATE + "', 'type': 'string'}, " +
            "{'name': '" + YtRow.F_REQUEST_ID + "', 'type': 'string'}, " +
            "{'name': '" + YtRow.F_URL + "', 'type': 'string'}, " +
            "{'name': '" + YtRow.F_ITEM + "', 'type': 'string'}, " +
            "{'name': '" + YtRow.F_CHECK_PARAMS + "', 'type': 'string'}]";


    @Override
    public Resp process(Req request) throws WebmasterException {
        List<UrlCheckDataBlock> failedBlocks = new ArrayList<>();
        Map<Pair<UUID, UrlCheckDataBlockType>, String> errorsMap = new HashMap<>();
        Set<Pair<UUID, String>> filterSet = new HashSet<>();

        urlCheckDataBlocksYDao.forEachWithoutData(b -> {
            if (b.getFetchState() == UrlCheckDataBlockState.ROBOT_FETCH_ERROR) {
                String errorStr = urlCheckDataBlocksYDao.getInternalError(b.getRequestId());
                if (errorStr == null || filterSet.contains(Pair.of(b.getRequestId(), errorStr))) {
                    return;
                }

                failedBlocks.add(b);
                errorsMap.put(Pair.of(b.getRequestId(), b.getBlockType()), errorStr);
                filterSet.add(Pair.of(b.getRequestId(), errorStr));
            }
        });

        failedBlocks.sort(Comparator.comparing(UrlCheckDataBlock::getAddDate).reversed());

        var failedRequestIds = failedBlocks.stream()
                .map(UrlCheckDataBlock::getRequestId)
                .distinct()
                .toList();

        Map<UUID, UrlCheckRequestParams> reqIdToReqParamsMap = new HashMap<>();
        failedRequestIds.forEach(reqId -> {
            var urlCheckParamsBlock = urlCheckDataBlocksYDao.get(reqId, UrlCheckDataBlockType.URL_CHECK_REQUEST_PARAMS);
            if (urlCheckParamsBlock == null) {
                return;
            }

            var urlCheckParams = JsonMapping.readValue(urlCheckParamsBlock.getData(), UrlCheckRequestParamsData.class).getUrlCheckRequestParams();
            reqIdToReqParamsMap.put(reqId, urlCheckParams);
        });

        try {
            var table = ytService.prepareTableData(ytTablePath.getName(), tw -> {
                failedBlocks.forEach(b -> {
                    var urlCheckParams = reqIdToReqParamsMap.get(b.getRequestId());
                    if (urlCheckParams == null) {
                        return;
                    }

                    tw.column(YtRow.F_ADD_DATE, b.getAddDate().toString());
                    tw.column(YtRow.F_REQUEST_ID, b.getRequestId().toString());
                    tw.column(YtRow.F_URL, urlCheckParams.getUrl());
                    tw.column(YtRow.F_ITEM, errorsMap.get(Pair.of(b.getRequestId(), b.getBlockType())));
                    tw.column(YtRow.F_CHECK_PARAMS, JsonMapping.writeValueAsString(urlCheckParams));
                    tw.rowEnd();
                });
            });

            YtTransactionService.TransactionProcess process = new YtUtils.TransactionWriterBuilder(ytTablePath, table)
                    .withSchema(TABLE_SCHEMA)
                    .build();

            YtUtils.TransactionExecutor writer = new YtUtils.TransactionExecutor(ytService, ytTablePath.getParent());
            writer.execute(process);
        } catch (Exception e) {
            throw new WebmasterException("Failed to write Yt table",
                    new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), "Failed to write Yt table"), e);
        }

        return new Resp();
    }


    private static class YtRow {
        static final String F_ADD_DATE = "AddDate";
        static final String F_REQUEST_ID = "RequestId";
        static final String F_URL = "Url";
        static final String F_ITEM = "Item";
        static final String F_CHECK_PARAMS = "CheckParams";
    }

    public static class Req implements ActionRequest {
    }

    public static class Resp implements ActionResponse.NormalResponse {
    }
}
