package ru.yandex.webmaster3.storage.verification;

import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import lombok.Setter;
import net.htmlparser.jericho.EndTag;
import net.htmlparser.jericho.Segment;
import net.htmlparser.jericho.Source;
import net.htmlparser.jericho.StartTag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.host.verification.IUserHostVerifier;
import ru.yandex.webmaster3.core.host.verification.PageUnavailableReason;
import ru.yandex.webmaster3.core.host.verification.UinUtil;
import ru.yandex.webmaster3.core.host.verification.VerificationCausedBy;
import ru.yandex.webmaster3.core.host.verification.VerificationFailInfo;
import ru.yandex.webmaster3.core.host.verification.VerificationHttpResponseInfo;
import ru.yandex.webmaster3.core.host.verification.fail.MetaTagNotFoundFailInfo;
import ru.yandex.webmaster3.core.host.verification.fail.PageUnavailableFailInfo;
import ru.yandex.webmaster3.core.util.Either;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.util.ZoraResponseDocumentUtil;
import ru.yandex.webmaster3.core.zora.data.response.ZoraUrlFetchResponse;
import ru.yandex.wmtools.common.error.InternalException;

/**
 * @author avhaliullin
 */
public class MetaTagVerifier implements IUserHostVerifier {
    private static final Logger log = LoggerFactory.getLogger(MetaTagVerifier.class);

    @Setter
    private VerificationPageDownloadService verificationPageDownloadService;
    @Setter
    private int allowedRedirects;

    @Override
    public Optional<VerificationFailInfo> verify(long userId, WebmasterHostId hostId, UUID recordId, long verificationUin, VerificationCausedBy verificationCausedBy) {
        URL urlAddress = getVerificationUrl(hostId);

        Either<ZoraUrlFetchResponse, VerificationFailInfo> downloadResult =
                verificationPageDownloadService.getVerificationPage(urlAddress,
                        allowedRedirects,
                        EnumSet.of(RedirectResolveFeature.ALLOW_OTHER_ORIGIN, RedirectResolveFeature.ALLOW_RELATIVE));
        if (downloadResult.isLeft()) {
            return doVerify(downloadResult.getLeft().get(), verificationUin, urlAddress);
        } else {
            return downloadResult.getRight();
        }
    }

    private Optional<VerificationFailInfo> doVerify(ZoraUrlFetchResponse response, long verificationUin, URL verificationUrl) {
        try {
            Source source = new Source(new StringReader(ZoraResponseDocumentUtil.getResponseString(response)));
            boolean inHeadTag = false;
            String expectedVerificationUid = UinUtil.getUinString(verificationUin);
            List<String> metaTags = new ArrayList<>();
            List<String> yandexVerificationTags = new ArrayList<>();
            for (Segment segment : source) {
                if (segment instanceof StartTag) {
                    StartTag startTag = (StartTag) segment;
                    if ("body".equalsIgnoreCase(startTag.getName())) {
                        break;
                    } else if ("head".equalsIgnoreCase(startTag.getName())) {
                        inHeadTag = true;
                    } else if (inHeadTag && "meta".equalsIgnoreCase(startTag.getName())) {
                        String name = startTag.getAttributeValue("name");
                        if ("yandex-verification".equalsIgnoreCase(name)) {
                            yandexVerificationTags.add(startTag.tidy(false));
                        } else {
                            metaTags.add(startTag.tidy(false));
                            continue;
                        }
                        String contentValue = startTag.getAttributeValue("content");
                        if (expectedVerificationUid.equalsIgnoreCase(contentValue)) {
                            log.info("Jericho. Checking meta-tag: Verification info in meta tag found successfully " + verificationUrl);
                            return Optional.empty();
                        } else {
                            log.debug("Jericho. Unknown verification uid: found content=" + contentValue + " expected=" + expectedVerificationUid + " " + verificationUrl);
                        }
                    }
                } else if (segment instanceof EndTag) {
                    if ("head".equalsIgnoreCase(((EndTag) segment).getName())) {
                        inHeadTag = false;
                    }
                }
            }

            log.info("Jericho. Valid meta-tag not found: " + verificationUrl);
            return Optional.of(
                    new MetaTagNotFoundFailInfo(
                            metaTags,
                            yandexVerificationTags,
                            VerificationHttpResponseInfo.createFromZoraResponse(response)
                    )
            );
        } catch (InternalException | IOException e) {
            log.warn("Jericho. Checking meta-tag: Not verified: IOException while reading file with meta-tag: " + verificationUrl, e);
            return Optional.of(
                    new PageUnavailableFailInfo(
                            verificationUrl,
                            VerificationHttpResponseInfo.createFromZoraResponse(response),
                            PageUnavailableReason.CONTENT_PROCESSING_ERROR
                    )
            );
        }
    }


    private static URL getVerificationUrl(WebmasterHostId hostId) {
        try {
            return new URL(IdUtils.hostIdToUrl(hostId));
        } catch (MalformedURLException e) {
            throw new RuntimeException("Unexpected error: failed to get main page url for host " + hostId);
        }
    }

    @Override
    public boolean isApplicable(WebmasterHostId hostId, long userId) {
        return true;
    }

}
