package ru.yandex.webmaster3.api.verification.action;

import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Component;
import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.api.http.auth.Permission;
import ru.yandex.webmaster3.api.http.rest.AbstractApiAction;
import ru.yandex.webmaster3.api.verification.data.*;
import ru.yandex.webmaster3.core.data.UserAddedHost;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.data.WebmasterUser;
import ru.yandex.webmaster3.core.host.verification.*;
import ru.yandex.webmaster3.core.host.verification.fail.PageUnavailableFailInfo;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.util.TimeUtils;
import ru.yandex.webmaster3.core.user.UserVerifiedHost;
import ru.yandex.webmaster3.storage.user.service.UserHostsService;
import ru.yandex.webmaster3.storage.verification.HostVerifierService;
import ru.yandex.webmaster3.storage.verification.HtmlFileVerifier;

import java.util.Optional;
import java.util.Set;

/**
 * @author avhaliullin
 */
@Description("Посмотреть информацию о подтверждении прав на хост")
@Category("verification")
@Component
public class HostVerificationInfoAction extends AbstractApiAction<HostVerificationInfoRequest, HostVerificationInfoResponse> {
    private UserHostsService userHostsService;
    private HostVerifierService hostVerifierService;

    public HostVerificationInfoAction() {
        super(Permission.COMMON);
    }

    @Override
    public HostVerificationInfoResponse process(HostVerificationInfoRequest request) {
        Set<ApiExplicitVerificationType> applicable = ApiExplicitVerificationType.fromVerificationTypes(
                hostVerifierService.listApplicable(request.getHostId(), request.getUserId(), VerificationTypeScope.API)
        );
        UserAddedHost userAddedHost = request.getUserAddedHost();
        if (!userAddedHost.isVerified()) {
            UserHostVerificationInfo verificationInfo =
                    userHostsService.getVerificationInfo(request.getUserId(), request.getHostId());
            VerificationStatus status = verificationInfo.getVerificationStatus();
            if (HostVerifierService.isVerificationExpired(verificationInfo)) {
                status = VerificationStatus.INTERNAL_ERROR;
            }
            Optional<ApiVerificationType> verificationTypeOpt =
                    verificationInfo.getVerificationType() == null ? Optional.empty() :
                            Optional.of(ApiVerificationType.fromVerificationType(verificationInfo.getVerificationType()));
            Optional<ApiVerificationFailInfo> failInfoOpt;
            failInfoOpt = verificationTypeOpt.flatMap(apiVerificationType -> getFailInfo(
                    verificationInfo,
                    apiVerificationType
            ));
            return new HostVerificationInfoResponse(
                    UinUtil.getUinString(verificationInfo.getVerificationUin()),
                    ApiVerificationState.fromVerificationStatus(status),
                    verificationTypeOpt,
                    Optional.ofNullable(verificationInfo.getLastAttempt()).map(d -> d.toDateTime(TimeUtils.EUROPE_MOSCOW_ZONE)),
                    failInfoOpt,
                    applicable
            );
        } else {
            UserVerifiedHost userVerifiedHost = userHostsService.getVerifiedHost(
                    new WebmasterUser(request.getUserId()),
                    request.getHostId()
            );
            return new HostVerificationInfoResponse(
                    UinUtil.getUinString(userVerifiedHost.getVerificationUin()),
                    ApiVerificationState.VERIFIED,
                    Optional.of(ApiVerificationType.fromVerificationType(userVerifiedHost.getVerificationType())),
                    Optional.of(userVerifiedHost.getVerificationDate().toDateTime(TimeUtils.EUROPE_MOSCOW_ZONE)),
                    Optional.empty(),
                    applicable
            );
        }
    }

    private Optional<ApiVerificationFailInfo> getFailInfo(UserHostVerificationInfo verificationInfo,
                                                          ApiVerificationType verificationType) {
        if (verificationInfo.getVerificationStatus() != VerificationStatus.VERIFICATION_FAILED) {
            return Optional.empty();
        }
        VerificationFailInfo failInfo = verificationInfo.getVerificationFailInfo();
        String uin = UinUtil.getUinString(verificationInfo.getVerificationUin());
        WebmasterHostId hostId = verificationInfo.getHostId();
        String message;
        ApiVerificationFailReason reason;

        // Следи за break'ами!
        switch (failInfo.getType()) {
            case META_TAG_NOT_FOUND:
                reason = ApiVerificationFailReason.META_TAG_NOT_FOUND;
                message = "Meta tag <meta name=\"yandex-verification\" content=\"" + uin + "\"> not found on page " +
                        IdUtils.hostIdToUrl(hostId);
                break;

            case PAGE_UNAVAILABLE:
                reason = ApiVerificationFailReason.PAGE_UNAVAILABLE;
                PageUnavailableFailInfo pageUnavailable = (PageUnavailableFailInfo) failInfo;
                message = "Page " + pageUnavailable.getUrl() + " was unavailable";
                break;

            case WRONG_HTML_PAGE_CONTENT:
                reason = ApiVerificationFailReason.WRONG_HTML_PAGE_CONTENT;
                message = "Required html content wasn't found on page " +
                        HtmlFileVerifier.getVerificationUrl(
                                hostId,
                                verificationInfo.getVerificationUin()
                        );
                break;

            case DNS_RECORD_NOT_FOUND:
                reason = ApiVerificationFailReason.DNS_RECORD_NOT_FOUND;
                message = "TXT-record \"yandex-verification: " + uin + "\" not found in DNS for domain " +
                        hostId.getPunycodeHostname();
                break;

            case NOT_APPLICABLE_FAIL_INFO:
                reason = ApiVerificationFailReason.NOT_APPLICABLE;
                message = "Verification type " + verificationType + " isn't available for your host";
                break;

            case WHOIS_EMAIL_NOT_FOUND:
                reason = ApiVerificationFailReason.WHOIS_EMAIL_NOT_FOUND;
                message = "Failed to find in whois any email verified by user";
                break;

            case DELEGATION_CANCELLED:
                reason = ApiVerificationFailReason.DELEGATION_CANCELLED;
                message = "Delegation was cancelled by other host owner";
                break;

            case PDD_VERIFICATION_CANCELLED:
                reason = ApiVerificationFailReason.PDD_VERIFICATION_CANCELLED;
                message = "Host isn't verified in https://pdd.yandex.ru/ anymore";
                break;

//        Не палимся
            case AUTO_VERIFICATION_CANCELLED:
            case IDM_VERIFICATION_CANCELLED:
            case SELF_VERIFICATION_CANCELLED:
            case TXT_VERIFICATION_NOT_SUPPORTED:
            case PDD_EMU_FAIL_INFO:
                reason = ApiVerificationFailReason.NOT_APPLICABLE;
                message = "Verification type " + verificationType + " isn't available for your host";
                break;

            default:
                throw new RuntimeException("Unknown verification fail reason " + failInfo.getType());
        }
        return Optional.of(new ApiVerificationFailInfo(reason, message));
    }

    @Required
    public void setUserHostsService(UserHostsService userHostsService) {
        this.userHostsService = userHostsService;
    }

    @Required
    public void setHostVerifierService(HostVerifierService hostVerifierService) {
        this.hostVerifierService = hostVerifierService;
    }
}
