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

import lombok.RequiredArgsConstructor;
import org.joda.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ru.yandex.webmaster3.api.addurl.RecrawlQueueItemLocator;
import ru.yandex.webmaster3.api.addurl.RecrawlQueueLocator;
import ru.yandex.webmaster3.api.addurl.RecrawlQuotaLocator;
import ru.yandex.webmaster3.api.addurl.action.AddRecrawlUrlAction;
import ru.yandex.webmaster3.api.addurl.action.GetRecrawlQuotaAction;
import ru.yandex.webmaster3.api.addurl.action.GetRecrawlRequestInfoAction;
import ru.yandex.webmaster3.api.addurl.action.ListRecrawlRequestsAction;
import ru.yandex.webmaster3.api.diagnostics.HostDiagnosticsLocator;
import ru.yandex.webmaster3.api.diagnostics.action.GetHostDiagnosticsAction;
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.*;
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.indexing2.Indexing2HistoryLocator;
import ru.yandex.webmaster3.api.indexing2.Indexing2SamplesLocator;
import ru.yandex.webmaster3.api.indexing2.action.Indexing2HistoryAction;
import ru.yandex.webmaster3.api.indexing2.action.Indexing2SamplesAction;
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.searchurls.SearchUrlEventSamplesLocator;
import ru.yandex.webmaster3.api.searchurls.SearchUrlEventsHistoryLocator;
import ru.yandex.webmaster3.api.searchurls.SearchUrlHistoryLocator;
import ru.yandex.webmaster3.api.searchurls.SearchUrlSamplesLocator;
import ru.yandex.webmaster3.api.searchurls.action.SearchUrlEventSamplesAction;
import ru.yandex.webmaster3.api.searchurls.action.SearchUrlEventsHistoryAction;
import ru.yandex.webmaster3.api.searchurls.action.SearchUrlHistoryAction;
import ru.yandex.webmaster3.api.searchurls.action.SearchUrlSamplesAction;
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.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
 */
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class ApiV40Router extends AbstractApiRouter {
    private static final Logger log = LoggerFactory.getLogger(ApiV40Router.class);

    private final GetUserAction getUserAction;

    private final GetHostListAction getHostListAction;
    private final AddHostAction addHostAction;
    private final DeleteHostAction deleteHostAction;
    private final HostInfoAction hostInfoAction;
    private final GetHostSummaryAction getHostSummaryAction;

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

    private final GetSitemapListAction getSitemapListAction;
    private final GetSitemapInfoAction getSitemapInfoAction;

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

    private final SqiHistoryAction sqiHistoryAction;

    private final ListQueriesAction listQueriesAction;

    private final ExternalLinkSamplesAction externalLinkSamplesAction;
    private final ExternalLinksHistoryAction externalLinksHistoryAction;

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

    private final AddRecrawlUrlAction addRecrawlUrlAction;
    private final ListRecrawlRequestsAction listRecrawlRequestsAction;
    private final GetRecrawlRequestInfoAction getRecrawlRequestInfoAction;
    private final GetRecrawlQuotaAction getRecrawlQuotaAction;

    private final GetHostDiagnosticsAction getHostDiagnosticsAction;

    private final Indexing2HistoryAction indexing2HistoryAction;
    private final Indexing2SamplesAction indexing2SamplesAction;

    private final SearchUrlHistoryAction searchUrlHistoryAction;
    private final SearchUrlSamplesAction searchUrlSamplesAction;
    private final SearchUrlEventsHistoryAction searchUrlEventsHistoryAction;
    private final SearchUrlEventSamplesAction searchUrlEventSamplesAction;

    @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(getHostSummaryAction)
        );
        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(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, ApiV40Router::encodeTurboTaskLocator, "какая-то строка")
                        .build(ApiV40Router::decodeTurboTaskLocator)
                        .onPost(addTurboPagesAction)
        );

        addResource(
                hostResource(RecrawlQueueLocator.class)
                        .path("recrawl").path("queue")
                        .build(RecrawlQueueLocator::new)
                        .onGet(listRecrawlRequestsAction)
                        .onPost(addRecrawlUrlAction)
        );
        addResource(
                hostResource(RecrawlQuotaLocator.class)
                        .path("recrawl").path("quota")
                        .build(RecrawlQuotaLocator::new)
                        .onGet(getRecrawlQuotaAction)
        );
        addResource(
                hostResource(RecrawlQueueItemLocator.class)
                        .path("recrawl").path("queue")
                        .pathParam("task-id", UrlParam.UUID_PARAM, RecrawlQueueItemLocator::getTaskId, "id задачи")
                        .build(RecrawlQueueItemLocator::new)
                        .onGet(getRecrawlRequestInfoAction)
        );


        addResource(
                hostResource(HostDiagnosticsLocator.class)
                        .path("diagnostics")
                        .build(HostDiagnosticsLocator::new)
                        .onGet(getHostDiagnosticsAction)
        );

        addResource(
                hostResource(Indexing2HistoryLocator.class)
                        .path("indexing")
                        .path("history")
                        .build(Indexing2HistoryLocator::new)
                        .onGet(indexing2HistoryAction)
        );
        addResource(
                hostResource(Indexing2SamplesLocator.class)
                        .path("indexing")
                        .path("samples")
                        .build(Indexing2SamplesLocator::new)
                        .onGet(indexing2SamplesAction)
        );

        addResource(
                hostResource(SearchUrlHistoryLocator.class)
                        .path("search-urls")
                        .path("in-search")
                        .path("history")
                        .build(SearchUrlHistoryLocator::new)
                        .onGet(searchUrlHistoryAction)
        );
        addResource(
                hostResource(SearchUrlSamplesLocator.class)
                        .path("search-urls")
                        .path("in-search")
                        .path("samples")
                        .build(SearchUrlSamplesLocator::new)
                        .onGet(searchUrlSamplesAction)
        );
        addResource(
                hostResource(SearchUrlEventsHistoryLocator.class)
                        .path("search-urls")
                        .path("events")
                        .path("history")
                        .build(SearchUrlEventsHistoryLocator::new)
                        .onGet(searchUrlEventsHistoryAction)
        );
        addResource(
                hostResource(SearchUrlEventSamplesLocator.class)
                        .path("search-urls")
                        .path("events")
                        .path("samples")
                        .build(SearchUrlEventSamplesLocator::new)
                        .onGet(searchUrlEventSamplesAction)
        );
    }

    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 сайта");
    }
}
