package ru.yandex.webmaster3.viewer.http.camelcase;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.http.WriteAction;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.storage.host.moderation.camelcase.service.HostDisplayNameService;
import ru.yandex.webmaster3.viewer.W3CheckHostNameService;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostAction;
import ru.yandex.webmaster3.viewer.http.camelcase.data.BeautyHostName;
import ru.yandex.wmtools.common.SupportedProtocols;
import ru.yandex.wmtools.common.util.URLUtil;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Optional;

/**
 * User: azakharov
 * Date: 16.07.14
 * Time: 17:42
 */
@WriteAction
@Description(value = "Сохранение заявки на изменение регистра имени сайта")
@Category("camelcase")
public class ChangeHostDisplayNameAction extends
        AbstractUserVerifiedHostAction<ChangeHostDisplayNameRequest, ChangeHostDisplayNameResponse> {
    private static final Logger log = LoggerFactory.getLogger(CancelHostDisplayNameChangeAction.class);

    private W3CheckHostNameService w3checkHostNameService;
    private HostDisplayNameService hostDisplayNameService;

    @Override
    public ChangeHostDisplayNameResponse process(ChangeHostDisplayNameRequest request) throws WebmasterException {
        final WebmasterHostId hostId = request.getHostId();
        if (hostId.isIDN()) {
            return new ChangeHostDisplayNameResponse.IdnHostsAreNotSupportedResponse(this.getClass());
        }

        final String newDisplayHostName;

        try {
            final Optional<URL> displayName = expandDisplayName(hostId, request.getDisplayName());
            if (!displayName.isPresent()) {
                hostDisplayNameService.hideRefusedRequestIfNecessary(hostId);
                return new ChangeHostDisplayNameResponse.InvalidDisplayNameResponse(this.getClass());
            }

            final URL displayNameUrl = displayName.get();
            if (displayNameUrl.getHost() != null) {
                newDisplayHostName = displayNameUrl.getHost();
            } else {
                newDisplayHostName = displayNameUrl.getAuthority();
            }
        } catch (MalformedURLException | URISyntaxException | SupportedProtocols.UnsupportedProtocolException e) {
            throw new WebmasterException("Illegal display name value",
                    new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(),
                            ChangeHostDisplayNameRequest.PARAM_DISPLAY_NAME, request.getDisplayName()), e);
        }

        final W3CheckHostNameService.DisplayNameCheckResult result = w3checkHostNameService.checkDisplayName(newDisplayHostName);
        final boolean autoModerated;

        switch (result) {
            case GOOD:
                autoModerated = true;
                break;

            case NEEDS_MODERATION:
                autoModerated = false;
                break;

            case ERROR_WRONG_GEO_NAME_CASE:
            case ERROR_WRONG_WWW_CASE:
            case ERROR_TLD_CASE_CHANGE:
            case ERROR_TOO_LITTLE_DOMAIN_NAME_PARTS:
                hostDisplayNameService.hideRefusedRequestIfNecessary(hostId);
                return new ChangeHostDisplayNameResponse.InvalidDisplayNameResponse(this.getClass());
            case ERROR_TOO_MANY_CONSECUTIVE_UPPER_LETTERS:
                hostDisplayNameService.hideRefusedRequestIfNecessary(hostId);
                return new ChangeHostDisplayNameResponse.TooManyConsecutiveUpperLettersResponse(this.getClass());
            default:
                log.error("Unsupported check result: {}, displayName={}", result, newDisplayHostName);
                return new ChangeHostDisplayNameResponse.InvalidDisplayNameResponse(this.getClass());
        }

        HostDisplayNameService.HostDisplayNameChangeResult changeResult =
                hostDisplayNameService.changeDisplayName(hostId, request.getUserId(), newDisplayHostName,
                        request.getUserComment(), autoModerated);
        switch (changeResult.status) {
            case ALREADY_HAS_MODERATION_REQUEST:
                return new ChangeHostDisplayNameResponse.AlreadyHasModerationRequestResponse(this.getClass());
            case ALREADY_HAS_PENDING_REQUEST:
                return new ChangeHostDisplayNameResponse.AlreadyHasPendingRequestResponse(newDisplayHostName, this.getClass());
            case DISPLAY_NAME_IS_THE_SAME:
                return new ChangeHostDisplayNameResponse.DisplayNameIsTheSameResponse(this.getClass());
            case OK:
                BeautyHostName displayName = w3checkHostNameService.getDisplayName(newDisplayHostName);
                return new ChangeHostDisplayNameResponse.NormalResponse(changeResult.webmasterHost, changeResult.displayNameRequest, displayName);
            default:
                String message = "Unknown display name change result status: " + changeResult.status;
                throw new WebmasterException(message,
                        new WebmasterErrorResponse.InternalUnknownErrorResponse(this.getClass(), message));
        }
    }

    private Optional<URL> expandDisplayName(WebmasterHostId hostId, String displayNameString) throws MalformedURLException, URISyntaxException, SupportedProtocols.UnsupportedProtocolException {
        final String hostUrl = IdUtils.hostIdToUrl(hostId);

        URL displayNameUrl = IdUtils.normalizeUrl(hostId, SupportedProtocols.getURL(displayNameString));
        String newDisplayUrlString = URLUtil.getHostName(displayNameUrl, true);

        if (!hostUrl.equalsIgnoreCase(newDisplayUrlString)) {
            if (!hostId.getPunycodeHostname().toLowerCase().startsWith(displayNameUrl.getHost().toLowerCase())) {
                //displayNameUrl is not prefix of hostname
                return Optional.empty();
            }

            String suffix = hostId.getPunycodeHostname().substring(displayNameUrl.getHost().length());
            if (!suffix.startsWith(".")) {
                // suffix starting with character not equals to segment separator is illegal
                return Optional.empty();
            }
            displayNameUrl = IdUtils.normalizeUrl(hostId, SupportedProtocols.getURL(displayNameUrl.getHost() + suffix));

            newDisplayUrlString = URLUtil.getHostName(displayNameUrl, true);

            if (!hostUrl.equalsIgnoreCase(newDisplayUrlString)) {
                return Optional.empty();
            }
        }

        return Optional.of(displayNameUrl);
    }

    @Required
    public void setW3checkHostNameService(W3CheckHostNameService w3checkHostNameService) {
        this.w3checkHostNameService = w3checkHostNameService;
    }

    @Required
    public void setHostDisplayNameService(HostDisplayNameService hostDisplayNameService) {
        this.hostDisplayNameService = hostDisplayNameService;
    }
}
