package ru.yandex.webmaster3.worker.video;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Range;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrSubstitutor;
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.WebmasterErrorResponse;
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.util.yt.AsyncTableReader;
import ru.yandex.webmaster3.storage.util.yt.YtException;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.util.yt.YtService;
import ru.yandex.webmaster3.storage.util.yt.YtTableReadDriver;
import ru.yandex.webmaster3.storage.video.VideohostOfferVideohostsYDao;
import ru.yandex.webmaster3.storage.yql.YqlFunctions;
import ru.yandex.webmaster3.storage.yql.YqlService;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

/**
 * @author vsedaikina
 * 08.11.21
 */

@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component
public class ImportVideohostsListPeriodicTask extends PeriodicTask<ImportVideohostsListPeriodicTask.TaskState> {
    private final YtService ytService;
    private final VideohostOfferVideohostsYDao videohostOfferVideohostsYDao;
    private final YqlService yqlService;

    @Value("${webmaster3.worker.video.import.videohosts.list_index.path}")
    private final YtPath tableVideohostsList;
    @Value("${webmaster3.worker.video.upload.videohosts.experiment.autoimport.path}")
    private final YtPath experimentAutoimportTable;
    @Value("${webmaster3.worker.video.upload.hosts.whitelist.path}")
    private final YtPath hostsWhitelist;
    @Value("${webmaster3.worker.video.upload.videohost_to_alert.path}")
    private final YtPath alertedVideohosts;

    private final int TOP_HOSTS_PERCENT = 5;

    @Override
    public Result run(UUID runId) throws Exception {
        log.info("Update videohostings list");
        ytService.withoutTransaction(cypressService -> {
            AsyncTableReader<YtRow> tableReader = new AsyncTableReader<>(
                    cypressService, tableVideohostsList, Range.all(),
                    YtTableReadDriver.createYSONDriver(YtRow.class)
            );

            int count = 0;
            try (AsyncTableReader.TableIterator<YtRow> iterator = tableReader.read()) {
                List<VideohostOfferVideohostsYDao.VideoHostInfo> res = new ArrayList<>();
                while (iterator.hasNext()) {
                    YtRow row = iterator.next();
                    res.add(new VideohostOfferVideohostsYDao.VideoHostInfo(row.hostUrl, row.platinum_urls, row.urls)); //?
                    if (res.size() > 5000) {
                        videohostOfferVideohostsYDao.batchInsert(res);
                        res.clear();
                    }
                    count++;
                }
                if (!res.isEmpty()) {
                    videohostOfferVideohostsYDao.batchInsert(res);
                }

                updateExperimentInfo((int)(count * TOP_HOSTS_PERCENT / 100.0));
            } catch (YtException | InterruptedException | IOException e) {
                throw new WebmasterException("Error reading Yt table",
                        new WebmasterErrorResponse.YTServiceErrorResponse(getClass(), e), e);
            }

            return true;
        });

        return new Result(TaskResult.SUCCESS);
    }

    private void updateExperimentInfo(int limit) {
        log.info("Update videohosts list for experiment limit = {}", limit);

        StrSubstitutor substitutor = new StrSubstitutor(Map.of(
                "CLUSTER", experimentAutoimportTable.getCluster(),
                "EXP_PATH", experimentAutoimportTable.toYqlPath(),
                "WHITELIST", hostsWhitelist.toYqlPath(),
                "SOURCE", tableVideohostsList.toYqlPath(),
                "LIMIT_VAL", limit,
                "CUT_WWWM", YqlFunctions.CUT_WWW_AND_M.getFunctionDef(),
                "ALERTED_HOSTS", alertedVideohosts.toYqlPath()
        ));

        yqlService.execute(
                substitutor.replace(
                        """
                                use ${CLUSTER};
                                ${CUT_WWWM}
                                $cut_hosts = (
                                select host, group
                                from (
                                SELECT 'TEST' as `group`, $CutWWWM(Url::GetHost(host))._2 as host
                                FROM ${SOURCE})
                                );
                                $union_result = (
                                select host, 'TEST' as group from (
                                SELECT * from hahn.`home/webmaster/test/video/videohost_whitelist`
                                Union all select * from $cut_hosts)
                                group by host);

                                insert into ${WHITELIST} WITH TRUNCATE
                                select * from $union_result;
                                insert into ${EXP_PATH} WITH TRUNCATE
                                select * from $union_result;

                                insert into ${ALERTED_HOSTS} WITH TRUNCATE
                                select distinct host from (
                                select host from $cut_hosts LIMIT ${LIMIT_VAL});
                                """
                )
        );
    }

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

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

    public static class TaskState implements PeriodicTaskState {
    }

    public static class YtRow {
        @JsonProperty("host")
        private final String hostUrl;
        @JsonProperty("platinum_urls")
        private final long platinum_urls;
        @JsonProperty("porn_urls")
        private final long porn_urls;
        @JsonProperty("urls")
        private final long urls;

        @JsonCreator
        public YtRow(String hostUrl, long platinum_urls, long porn_urls, long urls) {
            this.hostUrl = hostUrl;
            this.platinum_urls = platinum_urls;
            this.porn_urls = porn_urls;
            this.urls = urls;
        }
    }
}
