/*
 * This file is part of the Wildfire Chat package.
 * (c) Heavyrain2012 <heavyrain.lee@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package com.xiaoleilu.loServer.action;

import cn.wildfirechat.proto.WFCMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.MemorySessionStore;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.moquette.spi.impl.Utils;
import io.moquette.spi.impl.security.AES;
import io.moquette.spi.security.Tokenor;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import win.liyufan.im.ErrorCode;

import java.util.Base64;
import java.util.concurrent.Executor;

@Route("/route")
@HttpMethod("POST")
public class RouteAction extends Action {
    private static final Logger LOG = LoggerFactory.getLogger(RouteAction.class);
    @Override
    public void action(Request request, Response response) {
        if (request.getNettyRequest() instanceof FullHttpRequest) {

            response.setContentType("application/octet-stream");
            FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
            boolean b = false;
            if ("web".equalsIgnoreCase(fullHttpRequest.headers().get("p"))) {
                b = true;
            }

            final boolean base64Response = b;

            byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());

            String str = new String(bytes);
            try {
                bytes = Base64.getDecoder().decode(str);
            } catch (IllegalArgumentException e) {
                sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
                return;
            }

            String cid = fullHttpRequest.headers().get("cid");
            byte[] cbytes = Base64.getDecoder().decode(cid);
            cbytes = AES.AESDecrypt(cbytes, null, true);
            if (cbytes == null) {
                sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
                return;
            }
            cid = new String(cbytes);

            String uid = fullHttpRequest.headers().get("uid");
            byte[] ubytes = Base64.getDecoder().decode(uid);
            ubytes = AES.AESDecrypt(ubytes, null, true);
            if (ubytes == null) {
                sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
                return;
            }
            uid = new String(ubytes);


            MemorySessionStore.Session session = sessionsStore.sessionForClientAndUser(uid, cid);
            if (session == null) {
                ErrorCode errorCode = sessionsStore.createNewSession(uid, cid, true);
                if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
                    sendResponse(response, errorCode, null, base64Response);
                    return;
                }
                session = sessionsStore.sessionForClientAndUser(uid, cid);
            }


            if (session != null) {
                bytes = AES.AESDecrypt(bytes, session.getSecret(), true);
            } else {
                sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
                return;
            }


            if (bytes == null) {
                sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
                return;
            }


            try {
                WFCMessage.IMHttpWrapper wrapper = WFCMessage.IMHttpWrapper.parseFrom(bytes);
                String token = wrapper.getToken();
                String userId = Tokenor.getUserId(token.getBytes());
                LOG.info("RouteAction token={}, userId={}", token, userId);
                if (userId == null) {
                    sendResponse(response, ErrorCode.ERROR_CODE_TOKEN_ERROR, null, base64Response);
                } else {
                    RPCCenter.getInstance().sendRequest(userId, wrapper.getClientId(), wrapper.getRequest(), wrapper.getData().toByteArray(), userId, TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
                        @Override
                        public void onSuccess(byte[] result) {
                            sendResponse(response, null, result, base64Response);
                        }

                        @Override
                        public void onError(ErrorCode errorCode) {
                            sendResponse(response, errorCode, null, base64Response);
                        }

                        @Override
                        public void onTimeout() {
                            sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null, base64Response);
                        }

                        @Override
                        public Executor getResponseExecutor() {
                            return command -> {
                                ctx.executor().execute(command);
                            };
                        }
                    });
                }
            } catch (InvalidProtocolBufferException e) {
                sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null, false);
            }
        }
    }

    private void sendResponse(Response response, ErrorCode errorCode, byte[] contents, boolean base64Response) {
        response.setStatus(HttpResponseStatus.OK);
        if(contents == null) {
            ByteBuf ackPayload = Unpooled.buffer();
            ackPayload.ensureWritable(1).writeByte(errorCode.getCode());
            response.setContent(ackPayload);
        } else {
            response.setContent(contents);
        }
        response.send();
    }
}
