package ru.yandex.webmaster3.api.http.v3;

import org.joda.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.webmaster3.api.host.HostListLocator;
import ru.yandex.webmaster3.api.host.HostLocator;
import ru.yandex.webmaster3.api.host.HostSummaryLocator;
import ru.yandex.webmaster3.api.host.action.AddHostAction;
import ru.yandex.webmaster3.api.host.action.DeleteHostAction;
import ru.yandex.webmaster3.api.host.action.GetHostListAction;
import ru.yandex.webmaster3.api.host.action.HostInfoAction;
import ru.yandex.webmaster3.api.host.action.v32.GetHostSummaryV32Action;
import ru.yandex.webmaster3.api.http.common.request.locator.AbstractHostLocator;
import ru.yandex.webmaster3.api.http.common.request.locator.AbstractUserLocator;
import ru.yandex.webmaster3.api.http.rest.routing.AbstractApiRouter;
import ru.yandex.webmaster3.api.http.rest.routing.QueryStringParseException;
import ru.yandex.webmaster3.api.http.rest.routing.ResourceLocatorBuilder;
import ru.yandex.webmaster3.api.http.rest.routing.UrlParam;
import ru.yandex.webmaster3.api.http.util.StringObfuscationUtil;
import ru.yandex.webmaster3.api.indexing.IndexingHistoryLocator;
import ru.yandex.webmaster3.api.indexing.action.IndexingHistoryAction;
import ru.yandex.webmaster3.api.links.ExternalLinkSamplesLocator;
import ru.yandex.webmaster3.api.links.ExternalLinksHistoryLocator;
import ru.yandex.webmaster3.api.links.action.ExternalLinkSamplesAction;
import ru.yandex.webmaster3.api.links.action.ExternalLinksHistoryAction;
import ru.yandex.webmaster3.api.queries.QueriesListLocator;
import ru.yandex.webmaster3.api.queries.action.ListQueriesAction;
import ru.yandex.webmaster3.api.sitemap.SitemapListLocator;
import ru.yandex.webmaster3.api.sitemap.SitemapLocator;
import ru.yandex.webmaster3.api.sitemap.UserAddedSitemapListLocator;
import ru.yandex.webmaster3.api.sitemap.UserAddedSitemapLocator;
import ru.yandex.webmaster3.api.sitemap.action.*;
import ru.yandex.webmaster3.api.sqi.SqiHistoryLocator;
import ru.yandex.webmaster3.api.sqi.action.SqiHistoryAction;
import ru.yandex.webmaster3.api.tic.TicHistoryLocator;
import ru.yandex.webmaster3.api.tic.action.TicHistoryAction;
import ru.yandex.webmaster3.api.turbo.GetTurboPushTaskStatusLocator;
import ru.yandex.webmaster3.api.turbo.TaskUploadAddressLocator;
import ru.yandex.webmaster3.api.turbo.TurboPagesLocator;
import ru.yandex.webmaster3.api.turbo.UploadTurboTaskLocator;
import ru.yandex.webmaster3.api.turbo.action.*;
import ru.yandex.webmaster3.api.user.UserInfoLocator;
import ru.yandex.webmaster3.api.user.action.GetUserAction;
import ru.yandex.webmaster3.api.verification.VerificationInfoLocator;
import ru.yandex.webmaster3.api.verification.VerificationUsersLocator;
import ru.yandex.webmaster3.api.verification.action.HostVerificationInfoAction;
import ru.yandex.webmaster3.api.verification.action.UsersVerifiedHostAction;
import ru.yandex.webmaster3.api.verification.action.VerifyHostAction;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.util.IdUtils;

/**
 * @author avhaliullin
 */
public class ApiV32Router extends AbstractApiRouter {
    private static final Logger log = LoggerFactory.getLogger(ApiV32Router.class);

    private GetUserAction getUserAction;

    private GetHostListAction getHostListAction;
    private AddHostAction addHostAction;
    private DeleteHostAction deleteHostAction;
    private HostInfoAction hostInfoAction;
    private GetHostSummaryV32Action getHostSummaryV32Action;

    private HostVerificationInfoAction hostVerificationInfoAction;
    private VerifyHostAction verifyHostAction;
    private UsersVerifiedHostAction usersVerifiedHostAction;

    private GetSitemapListAction getSitemapListAction;
    private GetSitemapInfoAction getSitemapInfoAction;

    private GetUserAddedSitemapListAction getUserAddedSitemapListAction;
    private GetUserAddedSitemapAction getUserAddedSitemapAction;
    private AddSitemapAction addSitemapAction;
    private DeleteSitemapAction deleteSitemapAction;

    private IndexingHistoryAction indexingHistoryAction;

    private TicHistoryAction ticHistoryAction;
    private SqiHistoryAction sqiHistoryAction;

    private ListQueriesAction listQueriesAction;

    private ExternalLinkSamplesAction externalLinkSamplesAction;
    private ExternalLinksHistoryAction externalLinksHistoryAction;

    private GetTaskUploadAddressAction getTaskUploadAddressAction;
    private AddTurboPagesAction addTurboPagesAction;
    private GetTurboPushTaskStatusAction getTurboPushTaskStatusAction;
    private GetTurboPushListAction getTurboPushListAction;

    @Override
    public void setupRoutes() {
        addResource(
                locator(UserInfoLocator.class)
                        .path("user")
                        .build(UserInfoLocator::new)
                        .onGet(getUserAction)
        );

        addResource(
                locator(HostListLocator.class)
                        .path("user")
                        .pathParam("user-id", UrlParam.LONG, AbstractUserLocator::getUserId, "id пользователя")
                        .path("hosts")
                        .build(HostListLocator::new)
                        .onGet(getHostListAction)
                        .onPost(addHostAction)
        );
        addResource(
                hostResource(HostLocator.class)
                        .build(HostLocator::new)
                        .onGet(hostInfoAction)
                        .onDelete(deleteHostAction)
        );
        addResource(
                hostResource(HostSummaryLocator.class)
                        .path("summary")
                        .build(HostSummaryLocator::new)
                        .onGet(getHostSummaryV32Action)
        );
        addResource(
                hostResource(VerificationInfoLocator.class)
                        .path("verification")
                        .build(VerificationInfoLocator::new)
                        .onGet(hostVerificationInfoAction)
                        .onPost(verifyHostAction)
        );
        addResource(
                hostResource(VerificationUsersLocator.class)
                        .path("owners")
                        .build(VerificationUsersLocator::new)
                        .onGet(usersVerifiedHostAction)
        );
        addResource(
                hostResource(SitemapListLocator.class)
                        .path("sitemaps")
                        .queryOptParam("parent_id", UrlParam.SITEMAP_ID, SitemapListLocator::getParentId, "id родительского сайтмепа")
                        .build(SitemapListLocator::new)
                        .onGet(getSitemapListAction)
        );
        addResource(
                hostResource(SitemapLocator.class)
                        .path("sitemaps")
                        .pathParam("sitemap-id", UrlParam.SITEMAP_ID, SitemapLocator::getSitemapId, "id сайтмепа")
                        .build(SitemapLocator::new)
                        .onGet(getSitemapInfoAction)
        );
        addResource(
                hostResource(UserAddedSitemapListLocator.class)
                        .path("user-added-sitemaps")
                        .build(UserAddedSitemapListLocator::new)
                        .onGet(getUserAddedSitemapListAction)
                        .onPost(addSitemapAction)
        );
        addResource(
                hostResource(UserAddedSitemapLocator.class)
                        .path("user-added-sitemaps")
                        .pathParam("sitemap-id", UrlParam.SITEMAP_ID, UserAddedSitemapLocator::getSitemapId, "id сайтмепа")
                        .build(UserAddedSitemapLocator::new)
                        .onGet(getUserAddedSitemapAction)
                        .onDelete(deleteSitemapAction)
        );
        addResource(
                hostResource(IndexingHistoryLocator.class)
                        .path("indexing-history")
                        .build(IndexingHistoryLocator::new)
                        .onGet(indexingHistoryAction)
        );
        addResource(
                hostResource(TicHistoryLocator.class)
                        .path("tic-history")
                        .build(TicHistoryLocator::new)
                        .onGet(ticHistoryAction)
        );
        addResource(
                hostResource(SqiHistoryLocator.class)
                .path("sqi-history")
                .build(SqiHistoryLocator::new)
                .onGet(sqiHistoryAction)
        );
        addResource(
                hostResource(QueriesListLocator.class)
                        .path("search-queries")
                        .path("popular")
                        .build(QueriesListLocator::new)
                        .onGet(listQueriesAction)
        );
        addResource(
                hostResource(ExternalLinkSamplesLocator.class)
                        .path("links")
                        .path("external")
                        .path("samples")
                        .build(ExternalLinkSamplesLocator::new)
                        .onGet(externalLinkSamplesAction)
        );
        addResource(
                hostResource(ExternalLinksHistoryLocator.class)
                        .path("links")
                        .path("external")
                        .path("history")
                        .build(ExternalLinksHistoryLocator::new)
                        .onGet(externalLinksHistoryAction)
        );

        addResource(
                hostResource(TaskUploadAddressLocator.class)
                        .path("turbo")
                        .path("uploadAddress")
                        .build(TaskUploadAddressLocator::new)
                        .onGet(getTaskUploadAddressAction)
        );

        addResource(
                hostResource(TurboPagesLocator.class)
                        .path("turbo")
                        .path("tasks")
                        .build(TurboPagesLocator::new)
                        .onGet(getTurboPushListAction)
        );

        addResource(
                hostResource(GetTurboPushTaskStatusLocator.class)
                        .path("turbo")
                        .path("tasks")
                        .pathParam("task-id", UrlParam.UUID_PARAM, GetTurboPushTaskStatusLocator::getPushId, "id задачи")
                        .build(GetTurboPushTaskStatusLocator::new)
                        .onGet(getTurboPushTaskStatusAction)
        );

        addResource(
                locator(UploadTurboTaskLocator.class)
                        .path("upload")
                        .path("turbo")
                        .pathParam("upload-id", UrlParam.STRING, ApiV32Router::encodeTurboTaskLocator, "какая-то строка")
                        .build(ApiV32Router::decodeTurboTaskLocator)
                        .onPost(addTurboPagesAction)
        );
    }

    private static String encodeTurboTaskLocator(UploadTurboTaskLocator locator) {
        String serialized = locator.getUserId() + ";" + locator.getHostId().toString() + ";" + locator.getMode().toString() + ";" + locator.getValidUntil().getMillis();
        String result = StringObfuscationUtil.encodeASCII(serialized);
        log.info("Created upload id " + result);
        return result;
    }

    private static UploadTurboTaskLocator decodeTurboTaskLocator(String encoded) {
        try {
            String serialized = StringObfuscationUtil.decodeASCII(encoded);
            log.info("Upload id decoded: {}", serialized);
            String[] parts = serialized.split(";");
            if (parts.length != 4) {
                throw new QueryStringParseException.ValidationException(null, "upload-id", true, encoded);
            }
            return new UploadTurboTaskLocator(
                    Long.parseLong(parts[0]),
                    IdUtils.stringToHostId(parts[1]),
                    TurboPushMode.valueOf(parts[2]),
                    new Instant(Long.parseLong(parts[3]))
            );
        } catch (IllegalArgumentException e) {
            throw new QueryStringParseException.ValidationException(null, "upload-id", true, encoded);
        }
    }

    private <L extends AbstractHostLocator> ResourceLocatorBuilder.PathBuilder2<Long, WebmasterHostId, L> hostResource(Class<L> locator) {
        return locator(locator)
                .path("user")
                .pathParam("user-id", UrlParam.LONG, AbstractUserLocator::getUserId, "id пользователя")
                .path("hosts")
                .pathParam("host-id", UrlParam.HOST_ID, AbstractHostLocator::getHostId, "id сайта");
    }

    @Required
    public void setGetHostListAction(GetHostListAction getHostListAction) {
        this.getHostListAction = getHostListAction;
    }

    @Required
    public void setAddHostAction(AddHostAction addHostAction) {
        this.addHostAction = addHostAction;
    }

    @Required
    public void setDeleteHostAction(DeleteHostAction deleteHostAction) {
        this.deleteHostAction = deleteHostAction;
    }

    @Required
    public void setHostInfoAction(HostInfoAction hostInfoAction) {
        this.hostInfoAction = hostInfoAction;
    }

    @Required
    public void setHostVerificationInfoAction(HostVerificationInfoAction hostVerificationInfoAction) {
        this.hostVerificationInfoAction = hostVerificationInfoAction;
    }

    @Required
    public void setVerifyHostAction(VerifyHostAction verifyHostAction) {
        this.verifyHostAction = verifyHostAction;
    }

    @Required
    public void setUsersVerifiedHostAction(UsersVerifiedHostAction usersVerifiedHostAction) {
        this.usersVerifiedHostAction = usersVerifiedHostAction;
    }

    @Required
    public void setGetSitemapListAction(GetSitemapListAction getSitemapListAction) {
        this.getSitemapListAction = getSitemapListAction;
    }

    @Required
    public void setGetSitemapInfoAction(GetSitemapInfoAction getSitemapInfoAction) {
        this.getSitemapInfoAction = getSitemapInfoAction;
    }

    @Required
    public void setGetUserAddedSitemapListAction(GetUserAddedSitemapListAction getUserAddedSitemapListAction) {
        this.getUserAddedSitemapListAction = getUserAddedSitemapListAction;
    }

    @Required
    public void setGetUserAddedSitemapAction(GetUserAddedSitemapAction getUserAddedSitemapAction) {
        this.getUserAddedSitemapAction = getUserAddedSitemapAction;
    }

    @Required
    public void setAddSitemapAction(AddSitemapAction addSitemapAction) {
        this.addSitemapAction = addSitemapAction;
    }

    @Required
    public void setDeleteSitemapAction(DeleteSitemapAction deleteSitemapAction) {
        this.deleteSitemapAction = deleteSitemapAction;
    }

    @Required
    public void setGetHostSummaryV32Action(GetHostSummaryV32Action getHostSummaryV32Action) {
        this.getHostSummaryV32Action = getHostSummaryV32Action;
    }

    @Required
    public void setIndexingHistoryAction(IndexingHistoryAction indexingHistoryAction) {
        this.indexingHistoryAction = indexingHistoryAction;
    }

    @Required
    public void setTicHistoryAction(TicHistoryAction ticHistoryAction) {
        this.ticHistoryAction = ticHistoryAction;
    }


    @Required
    public void setSqiHistoryAction(SqiHistoryAction sqiHistoryAction) {
        this.sqiHistoryAction = sqiHistoryAction;
    }

    @Required
    public void setListQueriesAction(ListQueriesAction listQueriesAction) {
        this.listQueriesAction = listQueriesAction;
    }

    @Required
    public void setGetUserAction(GetUserAction getUserAction) {
        this.getUserAction = getUserAction;
    }

    @Required
    public void setExternalLinkSamplesAction(ExternalLinkSamplesAction externalLinkSamplesAction) {
        this.externalLinkSamplesAction = externalLinkSamplesAction;
    }

    @Required
    public void setExternalLinksHistoryAction(ExternalLinksHistoryAction externalLinksHistoryAction) {
        this.externalLinksHistoryAction = externalLinksHistoryAction;
    }

    @Required
    public void setAddTurboPagesAction(AddTurboPagesAction addTurboPagesAction) {
        this.addTurboPagesAction = addTurboPagesAction;
    }

    @Required
    public void setGetTurboPushTaskStatusAction(GetTurboPushTaskStatusAction getTurboPushTaskStatusAction) {
        this.getTurboPushTaskStatusAction = getTurboPushTaskStatusAction;
    }

    @Required
    public void setGetTurboPushListAction(GetTurboPushListAction getTurboPushListAction) {
        this.getTurboPushListAction = getTurboPushListAction;
    }

    @Required
    public void setGetTaskUploadAddressAction(GetTaskUploadAddressAction getTaskUploadAddressAction) {
        this.getTaskUploadAddressAction = getTaskUploadAddressAction;
    }
}
