package ru.yandex.wmconsole.viewer.mac.servantlet;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.common.framework.core.Servantlet;
import ru.yandex.common.framework.http.HttpServRequest;
import ru.yandex.common.framework.http.HttpServResponse;
import ru.yandex.common.util.xml.XmlConvertable;
import ru.yandex.webmaster.common.util.xml.SimpleXmlBuilder;
import ru.yandex.wmconsole.viewer.mac.MacService;

/**
 * @author aherman
 */
public class CreateMacServantlet implements Servantlet<HttpServRequest, HttpServResponse> {
    private static final Logger log = LoggerFactory.getLogger(CreateMacServantlet.class);

    static final String MESSAGE_PARAM = "message";
    static final String MAC_PARAM = "mac";

    private MacService macService;

    @Override
    public void process(HttpServRequest req, HttpServResponse res) {
        String message = req.getParam(MESSAGE_PARAM, true);
        if (StringUtils.isEmpty(message)) {
            res.addData(new ErrorResponse(ErrorType.PARAMETER_MISSING, "Parameter: " + MESSAGE_PARAM));
            return;
        }

        byte[] messageBytes;
        try {
            messageBytes = message.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("Unable to convert message: " + message, e);
            res.setHttpStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            res.addData(new ErrorResponse(ErrorType.INTERNAL_ERROR, e.getMessage()));
            return;
        }

        String mac;
        try {
            mac = macService.createMac(messageBytes);
        } catch (InvalidKeyException e) {
            log.error("Mac generation failed: " + message, e);
            res.setHttpStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            res.addData(new ErrorResponse(ErrorType.INTERNAL_ERROR, e.getMessage()));
            return;
        }

        res.addData(new NormalResponse(message, mac));
    }

    static class ErrorResponse implements XmlConvertable {
        private final ErrorType errorType;
        private final String message;

        ErrorResponse(ErrorType errorType) {
            this(errorType, null);
        }

        ErrorResponse(ErrorType errorType, @Nullable String message) {
            this.errorType = errorType;
            this.message = message;
        }

        @Override
        public void toXml(StringBuilder storage) {
            SimpleXmlBuilder xml = new SimpleXmlBuilder(storage, false);
            xml.open("error").attribute("type", errorType);
            xml.element("message", message);
            xml.close();
        }
    }

    static class NormalResponse implements XmlConvertable {
        private final String message;
        private final String mac;
        private final Boolean macValid;

        NormalResponse(String message, String mac) {
            this(message, mac, null);
        }

        NormalResponse(String message, String mac, Boolean macValid) {
            this.message = message;
            this.mac = mac;
            this.macValid = macValid;
        }

        @Override
        public void toXml(StringBuilder storage) {
            SimpleXmlBuilder xml = new SimpleXmlBuilder(storage, false);

            xml.open("secret");
            xml.element("message", message);
            xml.element("mac", mac);
            xml.element("mac-valid", macValid);
            xml.close(); // secret
        }
    }

    static enum ErrorType {
        INTERNAL_ERROR,
        PARAMETER_MISSING,
        MAC_CREATE_FAILED,
        MAC_CHECK_FAILED
    }

    @Required
    public void setMacService(MacService macService) {
        this.macService = macService;
    }
}
