package ru.yandex.webmaster3.api.importanturls.data;

import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joda.time.DateTime;
import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.api.indexing2.data.IndexingStatusEnum;
import ru.yandex.webmaster3.api.searchurls.data.ApiExcludedSearchUrlInfo;
import ru.yandex.webmaster3.api.searchurls.data.ApiExcludedUrlStatusEnum;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.util.TimeUtils;
import ru.yandex.webmaster3.storage.importanturls.data.ImportantUrl;
import ru.yandex.webmaster3.storage.importanturls.data.ImportantUrlStatus;
import ru.yandex.wmtools.common.util.http.YandexHttpStatus;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * @author leonidrom
 */
@RequiredArgsConstructor
public class ApiImportantUrl {
    private final String url;

    @Nullable
    private final DateTime updateDate;

    private final List<ApiImportantUrlChangeIndicator> changeIndicators;
    private final Optional<IndexingStatus> indexingStatus;
    private final Optional<SearchStatus> searchStatus;

    @NotNull
    public static ApiImportantUrl fromCoreImportantUrl(ImportantUrl importantUrl) {
        String url = IdUtils.hostIdToUrl(importantUrl.getHostId()) + importantUrl.getRelativeUrl();
        ImportantUrlStatus lastStatus = importantUrl.getLastStatus();
        return fromCoreImportantUrlStatus(url, lastStatus);
    }

    @NotNull
    public static ApiImportantUrl fromCoreImportantUrlStatus(String url, ImportantUrlStatus status) {
        if (status == null) {
            return new ApiImportantUrl(url, null, Collections.emptyList(), Optional.empty(), Optional.empty());
        }

        DateTime updateDate = toApiDateTime(status.getRealUpdateTime());
        List<ApiImportantUrlChangeIndicator> changes = ApiImportantUrlChangeIndicator.fromCoreImportantUrlStatus(status);
        IndexingStatus indexingStatus = IndexingStatus.fromCoreImportantUrlStatus(status);
        SearchStatus searchStatus = SearchStatus.fromCoreImportantUrlStatus(status);

        return new ApiImportantUrl(url, updateDate, changes,
                Optional.ofNullable(indexingStatus), Optional.ofNullable(searchStatus));
    }

    public String getUrl() {
        return url;
    }

    public DateTime getUpdateDate() {
        return updateDate;
    }

    public List<ApiImportantUrlChangeIndicator> getChangeIndicators() {
        return changeIndicators;
    }

    public Optional<IndexingStatus> getIndexingStatus() {
        return indexingStatus;
    }

    public Optional<SearchStatus> getSearchStatus() {
        return searchStatus;
    }

    @RequiredArgsConstructor
    // по аналогии с ApiIndexedPageSample
    public static class IndexingStatus {
        private final IndexingStatusEnum status;
        private final Optional<Integer> httpCode;
        private final DateTime accessDate;

        @Nullable
        private static IndexingStatus fromCoreImportantUrlStatus(@Nullable ImportantUrlStatus status) {
            ImportantUrlStatus.IndexingInfo indexingInfo;
            if (status == null || (indexingInfo = status.getIndexingInfo()) == null) {
                return null;
            }

            var codeInfo = indexingInfo.getHttpCode();
            Optional<Integer> httpCode = Optional.ofNullable(YandexHttpStatus.toStandardHttpCode(codeInfo.getStatus()));
            DateTime lastAccess = toApiDateTime(indexingInfo.getLastAccess());

            return new IndexingStatus(IndexingStatusEnum.of(codeInfo.getCode()), httpCode, lastAccess);
        }

        @Description("обобщенный статус загрузки")
        public IndexingStatusEnum getStatus() {
            return status;
        }

        @Description("http-код полученный Роботом при обходе страницы, если есть")
        public Optional<Integer> getHttpCode() {
            return httpCode;
        }

        @Description("Дата обхода страницы Роботом")
        public DateTime getAccessDate() {
            return accessDate;
        }
    }

    @RequiredArgsConstructor
    // по аналогии с ApiSearchEventSample
    public static class SearchStatus {
        @Nullable
        private final String title;

        @Nullable
        private final String description;

        @Nullable
        private final DateTime lastAccess;

        @Nullable
        private final ApiExcludedSearchUrlInfo excludedInfo;

        @Nullable
        private static SearchStatus fromCoreImportantUrlStatus(@Nullable ImportantUrlStatus status) {
            ImportantUrlStatus.SearchInfo searchInfo;
            if (status == null || (searchInfo = status.getSearchInfo()) == null) {
                return null;
            }

            var urlStatusInfo = searchInfo.getUrlSearchInfo();
            var excludedInfo = ApiExcludedSearchUrlInfo.fromUrlStatusInfo(urlStatusInfo);
            var lastAccess = toApiDateTime(searchInfo.getLastAccess());

            return new SearchStatus(status.getTitle(), urlStatusInfo.getDescription(), lastAccess, excludedInfo);
        }

        @Description("заголовок страницы")
        public String getTitle() {
            return title;
        }

        @Description("description страницы")
        public String getDescription() {
            return description;
        }

        @Description("дата последнего обхода страницы на момент добавления ее в поиск/исключения из поиска")
        public DateTime getLastAccess() {
            return lastAccess;
        }

        @Description("находится ли данная страница в поиске")
        public boolean isSearchable() {
            return excludedInfo == null;
        }

        @Description("причина исключения страницы с поиска")
        public Optional<ApiExcludedUrlStatusEnum> getExcludedUrlStatus() {
            return Optional.ofNullable(excludedInfo).map(ApiExcludedSearchUrlInfo::getExcludedUrlStatus);
        }

        @Description("HTTP-код овета страницы для статуса HTTP_ERROR")
        public Optional<Integer> getBadHttpStatus() {
            return Optional.ofNullable(excludedInfo).map(ApiExcludedSearchUrlInfo::getBadHttpStatus);
        }

        @Description("другой адрес, по которому страница известна роботу - цель редиректа, значение мета-тега canonical или основной контентный дубль")
        public Optional<URL> getTargetUrl() {
            return Optional.ofNullable(excludedInfo).map(ApiExcludedSearchUrlInfo::getTargetUrl);
        }
    }

    @Nullable
    private static DateTime toApiDateTime(@Nullable DateTime date) {
        if (date == null) {
            return null;
        }

        return date.toDateTime(TimeUtils.EUROPE_MOSCOW_ZONE).withTimeAtStartOfDay();
    }
}
