package com.eactive.eai.adapter.controller;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONValue;
import org.springframework.web.filter.OncePerRequestFilter;

import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterUri;
import com.eactive.eai.common.stdmessage.STDMessageManager;
import com.eactive.eai.common.stdmessage.STDMsgInfoAddOnVO;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;


public class ApiRequestBodyFilter extends OncePerRequestFilter {
	
	static final String DJB_ROOTLESS_ARRAY = "{ \"DJB_ROOTLESS_ARRAY\" : ";

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
    	if (!isTarget(request)) {
            filterChain.doFilter(request, response);
            return;
        }

        //원본 바디를 byte[]로 읽기 (개행 포함 그대로) 왜? 뒤에 필터에서 HMAC값 계산할수도 있어서 변조되면 안됨. 개행문자도 그대로 가야함.
        byte[] rawBody = readBodyAsBytes(request);

        if (rawBody == null || rawBody.length == 0) {
            filterChain.doFilter(request, response);
            return;
        }

        byte[] finalBody = rawBody;

        try {
        	if(isJsonArray(rawBody)) {
        		String encoding = getInboundGroupAdapterEncoding(request);
            	String bodyStr = new String(rawBody, encoding);
            	Object json = JSONValue.parse(bodyStr);

	            if (json instanceof JSONArray) {
	            	bodyStr = DJB_ROOTLESS_ARRAY + bodyStr + "}";
	            	finalBody = bodyStr.getBytes(encoding);
	            }
        	}

        } catch (Exception e) {
            // JSON 파싱 실패 시 원본 유지
            finalBody = rawBody;
        }

        // Wrapper로 재주입
        HttpServletRequest wrapped =
                new CachedBodyHttpServletRequest(request, finalBody);

        filterChain.doFilter(wrapped, response);
    }
    
    public static boolean isJsonArray(byte[] json) {
        try (JsonParser parser = new JsonFactory().createParser(json)) {
            return parser.nextToken() == JsonToken.START_ARRAY;
        } catch (Exception e) {
            return false;
        }
    }

	private boolean isTarget(HttpServletRequest request) {
		String uri = request.getRequestURI();
		// token 이 포함되면 무조건 제외
		if (uri.contains("/token")) {
			return false;
		} else {
			return true;
		}
	}


    private byte[] readBodyAsBytes(HttpServletRequest request) throws IOException {
        try (ServletInputStream is = request.getInputStream();
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[4096];
            int len;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return baos.toByteArray();
        }
    }
    
    private String getInboundGroupAdapterEncoding(HttpServletRequest request) {
    	String apiUri = request.getRequestURI();
        apiUri = StringUtils.removeStart(apiUri, request.getContextPath());
        apiUri = StringUtils.removeEnd(apiUri, "/");

        String methodAndUri = request.getMethod() + "|" + apiUri;
        String adapterGrpName;
        HttpDynamicInAdapterUri adptUri = null;
        
		try {
			STDMessageManager manager = STDMessageManager.getInstance();
			STDMsgInfoAddOnVO stdMsgInfo = manager.getStdMsgInfoAddOn(methodAndUri);
			adapterGrpName = stdMsgInfo.gAdapterGroupName();

			adptUri = findAdptUri(apiUri, HttpDynamicInAdapterManager.getInstance(), adapterGrpName);

			AdapterGroupVO adapterGroupVO = AdapterManager.getInstance().getAdapterGroupVO(adptUri.getAdptGrpName());

			if (adapterGroupVO == null) {
				return Charset.defaultCharset().name();
			}

			return StringUtils.isNotBlank(adapterGroupVO.getMessageEncode()) ? adapterGroupVO.getMessageEncode()
					: Charset.defaultCharset().name(); // 서버 jvm file.encoding 확인, file.encoding=utf-8 주고있음.

		} catch (Exception e) {
			logger.warn("Received a transaction with an unknown interface ID", e);
			return Charset.defaultCharset().name();// utf-8
		}
    	
    }
    
    private HttpDynamicInAdapterUri findAdptUri(String apiUri, HttpDynamicInAdapterManager manager, String adapterGrpName) throws Exception {
        if (StringUtils.isBlank(apiUri)) {
            return null;
        }      
        

        for (int i = 0; i < 10; i++) {
            HttpDynamicInAdapterUri adptUri = manager.getAdptUri(apiUri);
            if (adptUri != null) {
            	if (adptUri.getAdptGrpName().equals(adapterGrpName)) {
            		return adptUri;
            	}
            }

            apiUri = StringUtils.substringBeforeLast(apiUri, "/");
            if (apiUri.length() < 4) { // /api
                return null;
            }
        }

        return null;
    }
}
