package ru.yandex.webmaster3.worker.feeds;

import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import ru.yandex.webmaster3.core.feeds.feed.ExternalFeedInfo;
import ru.yandex.webmaster3.core.feeds.feed.ExternalFeedType;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskState;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskType;
import ru.yandex.webmaster3.core.worker.task.TaskResult;
import ru.yandex.webmaster3.storage.feeds.FeedsExternalYDao;
import ru.yandex.webmaster3.storage.host.CommonDataState;
import ru.yandex.webmaster3.storage.settings.SettingsService;
import ru.yandex.webmaster3.storage.util.ydb.YdbYqlService;
import ru.yandex.webmaster3.storage.util.yt.YtNode;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.util.yt.YtService;
import ru.yandex.webmaster3.storage.yql.YqlService;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

import static ru.yandex.webmaster3.storage.host.CommonDataType.LAST_IMPORTED_EXTERNAL_FEEDS;


/**
 * @author kravchenko99
 * @date 8/19/21
 */


@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component
public class ImportExternalFeedsTask extends PeriodicTask<ImportExternalFeedsTask.TaskState> {

    private final YtService ytService;
    private final YqlService yqlService;
    private final SettingsService settingsService;
    private final FeedsExternalYDao feedsExternalYDao;
    private final YdbYqlService ydbYqlService;
    @Value("${webmaster3.worker.feeds.externalFeeds.offers}")
    private YtPath tablePath;
    @Value("${external.yt.service.hahn.root.default}/tmp/feeds/external_feeds")
    private YtPath tmpTable;



    private static final Set<ExternalFeedType> TYPES_FOR_IMPORT = EnumSet.of(ExternalFeedType.MARKET,
            ExternalFeedType.DIRECT, ExternalFeedType.TURBO);

    public static final String GET_INT_TYPE_QUERY = "$get_int_type = ($source) -> {\n" +
            "return CASE\n" +
            TYPES_FOR_IMPORT.stream().map(x -> "WHEN $source = '" + x.name() + "' THEN " + x.value()).collect(Collectors.joining("\n")) +
            "\nELSE 0" +
            "\nEND" +
            "};";



    public static final String DATA_SELECT_QUERY = "SELECT \n" +
            "            cast(domain as Utf8) as domain,\n" +
            "            cast(feed_url as Utf8) as url,\n" +
            "            cast($get_int_type(source) as Int32) as type,\n" +
            "            cast(${CURRENT_TIMESTAMP} as Timestamp) as update_date,\n" +
            "        FROM ${SOURCE_TABLE}";


    @Override
    public Result run(UUID runId) throws Exception {

        ytService.inTransaction(tablePath).execute(cypressService -> {
            YtNode node = cypressService.getNode(tablePath);
            if (node == null) {
                return false;
            }
            final DateTime modificationTime = new DateTime(node.getNodeMeta()
                    .get("modification_time")
                    .asText());
            CommonDataState settings = settingsService.getSettingUncached(LAST_IMPORTED_EXTERNAL_FEEDS);
            if (settings != null && new DateTime(settings.getValue()).equals(modificationTime)) {
                return false;
            }
            yqlService.execute(
                    "$remove_mobile = ($url) -> { \n" +
                            "    return if(SUBSTRING($url, 0, 2) == \"m.\", SUBSTRING($url, 2), $url)\n" +
                            "};\n" +
                            "\n" +
                            "$extract_domain = ($url) -> {\n" +
                            "\treturn $remove_mobile(Url::CutWWW2(Url::GetHost($url)))\n" +
                            "};" +
                            "INSERT INTO " + tmpTable.toYqlPath() + " WITH TRUNCATE\n" +
                            "SELECT DISTINCT $extract_domain(url) as domain, feed_url, source FROM " + tablePath.toYqlPath() +
                            "WHERE feed_url is not null and source in " + TYPES_FOR_IMPORT.stream().map(x -> "'" + x.name() + "'").collect(Collectors.joining(",", "(", ")"))
            );
            StrSubstitutor substitutor = new StrSubstitutor(Map.of(
                    "CURRENT_TIMESTAMP", String.valueOf(modificationTime.getMillis()),
                    "SOURCE_TABLE", tmpTable.toYqlPath()
            ));
            ydbYqlService.importToYdb(feedsExternalYDao.getTablePath(), substitutor.replace(DATA_SELECT_QUERY), GET_INT_TYPE_QUERY);
            feedsExternalYDao.deleteBefore(modificationTime);
            settingsService.update(LAST_IMPORTED_EXTERNAL_FEEDS, modificationTime.toString());
            return true;
        });
        return new Result(TaskResult.SUCCESS);
    }

    @lombok.Value
    private static class Row {
        @JsonProperty("domain")
        String domain;
        @JsonProperty("feed_url")
        String url;
        @JsonProperty("source")
        ExternalFeedType type;

        ExternalFeedInfo toInfo() {
            return new ExternalFeedInfo(domain, url, type);
        }
    }

    @Getter
    public static class TaskState implements PeriodicTaskState {

    }

    @Override
    public PeriodicTaskType getType() {
        return PeriodicTaskType.FEEDS_IMPORT_EXTERNAL_FEEDS;
    }

    @Override
    public TaskSchedule getSchedule() {
        //0 */5 * * * *
        return TaskSchedule.never();
//        return TaskSchedule.startByCron("0 11 * * * *");
    }
}
