package ru.yandex.webmaster3.viewer.http.url.checker3.data;

import org.joda.time.DateTime;
import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.core.data.HttpCodeInfo;
import ru.yandex.webmaster3.core.util.json.JsonMapping;
import ru.yandex.webmaster3.storage.crawl.RotorSettings;
import ru.yandex.webmaster3.storage.searchurl.samples.data.SearchUrlCanonicalStatus;
import ru.yandex.webmaster3.storage.searchurl.samples.data.UrlStatusInfo;
import ru.yandex.webmaster3.storage.url.checker3.data.UrlCheckDataBlock;
import ru.yandex.webmaster3.storage.url.checker3.data.UrlCheckDataBlockState;
import ru.yandex.webmaster3.storage.url.checker3.data.blocks.IndexingInfoData;
import ru.yandex.webmaster3.storage.url.checker3.data.blocks.SearchInfoData;

/**
 * @author leonidrom
 */
public class SearchInfoResponseDataBlock extends AbstractAsyncResponseDataBlock {
    private final String title;
    private final String description;
    private final UrlStatus urlStatus;;
    private final SearchInfo searchInfo;
    private final IndexingInfo indexingInfo;

    public SearchInfoResponseDataBlock(
            String title, String description,
            UrlStatus urlStatus, SearchInfo searchInfo, IndexingInfo indexingInfo) {
        super(UrlCheckDataBlockState.DONE);

        this.title = title;
        this.description = description;
        this.urlStatus = urlStatus;
        this.searchInfo = searchInfo;
        this.indexingInfo = indexingInfo;
    }

    public SearchInfoResponseDataBlock(UrlCheckDataBlockState state) {
        super(state);

        this.title = null;
        this.description = null;
        this.urlStatus = null;
        this.searchInfo = null;
        this.indexingInfo = null;
    }

    @Description("Статус страницы в базах обхода роботом и поиска")
    public enum UrlStatus  {
        NOT_INDEXED_AND_NOT_IN_SEARCH,
        IN_SEARCH,
        INDEXED_AND_NOT_IN_SEARCH,
        ;

        public static UrlStatus from(IndexingInfoData.IndexingInfo indexingInfo, SearchInfoData.SearchInfo searchInfo) {
            if (searchInfo != null) {
                // либо нет расширенного статуса (для старых данных), либо в поиске
                if (searchInfo.getExcludedStatus() == null || searchInfo.getExcludedStatus().getStatus().isSearchable()) {
                    return IN_SEARCH;
                } else {
                    return INDEXED_AND_NOT_IN_SEARCH;
                }
            }

            if (indexingInfo == null) {
                return NOT_INDEXED_AND_NOT_IN_SEARCH;
            }

            // indexingInfo != null && searchInfo == null
            return INDEXED_AND_NOT_IN_SEARCH;
        }
    }

    @Description("Опциональные данные о поиске, как в ответе /urlchecker2/report")
    public static class SearchInfo {
        private final HttpCodeInfo httpCode;
        private final DateTime lastAccess;
        private final UrlStatusInfo excludedStatus;
        private final SearchUrlCanonicalStatus canonicalStatus;

        private SearchInfo(HttpCodeInfo httpCode, DateTime lastAccess, UrlStatusInfo excludedStatus, String url) {
            this.httpCode = httpCode;
            this.lastAccess = lastAccess;
            this.excludedStatus = excludedStatus;
            this.canonicalStatus = SearchUrlCanonicalStatus.fromUrlAndCanonical(url, excludedStatus);
        }

        @Description("Код на момент построения поисковой базы")
        public HttpCodeInfo getHttpCode() {
            return httpCode;
        }

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

        @Description("Опциональная информация для страницы, исключенной из поиска")
        public UrlStatusInfo getExcludedStatus() {
            return excludedStatus;
        }

        @Description("Статус по каноникалу")
        public SearchUrlCanonicalStatus getCanonicalStatus() {
            return canonicalStatus;
        }

        public static SearchInfoResponseDataBlock.SearchInfo from(String url, SearchInfoData.SearchInfo searchInfo) {
            // по договоренности с фронтом в случае фейковых страниц не отдаем ничего
            if (searchInfo == null || searchInfo.isFake()) {
                return null;
            }

            return new SearchInfoResponseDataBlock.SearchInfo(new HttpCodeInfo(searchInfo.getHttpCode()),
                    searchInfo.getLastAccess(), searchInfo.getExcludedStatus(), url);
        }
    }

    @Description("Опциональные данные об обходе роботом, как в ответе /urlchecker2/report")
    public static class IndexingInfo {
        private final RotorSettings.RenderSettings usedRenderSettings;
        private final HttpCodeInfo httpCode;
        private final DateTime lastAccess;

        private IndexingInfo(RotorSettings.RenderSettings usedRenderSettings, HttpCodeInfo httpCode, DateTime lastAccess) {
            this.httpCode = httpCode;
            this.lastAccess = lastAccess;
            this.usedRenderSettings = usedRenderSettings;
        }

        @Description("Код последнего переобхода роботом")
        public HttpCodeInfo getHttpCode() {
            return httpCode;
        }

        @Description("Дата последнего переобхода роботом")
        public DateTime getLastAccess() {
            return lastAccess;
        }

        @Description("Использованные настройки исполнения JS")
        public RotorSettings.RenderSettings getUsedRenderSettings() {
            return usedRenderSettings;
        }

        private static IndexingInfo from(IndexingInfoData.IndexingInfo indexingInfo, SearchInfoData.SearchInfo searchInfo) {
            if (indexingInfo == null && searchInfo == null) {
                return null;
            }

            var usedRenderSettings = searchInfo.isProcessedByRotor()?
                    RotorSettings.RenderSettings.RENDER_ON : RotorSettings.RenderSettings.RENDER_OFF;
            if (indexingInfo != null) {
                return new IndexingInfo(usedRenderSettings, new HttpCodeInfo(indexingInfo.getHttpCode()),
                        new DateTime(indexingInfo.getLastAccess() * 1000L));
            } else {
                // если сведения о странице есть в поиске, то она хоть раза была обойдена роботом
                return new IndexingInfo(usedRenderSettings, new HttpCodeInfo(searchInfo.getHttpCode()),
                        searchInfo.getLastAccess());
            }
        }
    }

    public static SearchInfoResponseDataBlock fromDataBlocks(String url, UrlCheckDataBlock searchInfoDataBlock,
                                                             UrlCheckDataBlock indexingInfoDataBlock) {
        var state = searchInfoDataBlock.getFetchState().combineWith(indexingInfoDataBlock.getFetchState());
        if (!state.isDone()) {
            return new SearchInfoResponseDataBlock(state);
        }

        var searchInfoData = JsonMapping.readValue(searchInfoDataBlock.getData(), SearchInfoData.class);
        var indexingInfoData = JsonMapping.readValue(searchInfoDataBlock.getData(), IndexingInfoData.class);

        return new SearchInfoResponseDataBlock(
                searchInfoData.getTitle(),
                searchInfoData.getDescription(),
                SearchInfoResponseDataBlock.UrlStatus.from(indexingInfoData.getIndexingInfo(), searchInfoData.getSearchInfo()),
                SearchInfoResponseDataBlock.SearchInfo.from(url, searchInfoData.getSearchInfo()),
                SearchInfoResponseDataBlock.IndexingInfo.from(indexingInfoData.getIndexingInfo(), searchInfoData.getSearchInfo())
        );
    }

    @Description("Title страницы")
    public String getTitle() {
        return title;
    }

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

    @Description("Статус страницы в база обхода и поиска, как в ответе /urlchecker2/report")
    public UrlStatus getUrlStatus() {
        return urlStatus;
    }

    @Description("Опциональные данные о поиске, как в ответе /urlchecker2/report")
    public SearchInfo getSearchInfo() {
        return searchInfo;
    }

    @Description("Опциональные данные об обходе роботом, как в ответе /urlchecker2/report")
    public IndexingInfo getIndexingInfo() {
        return indexingInfo;
    }
}
