package com.izouma.awesomeadmin.web;

import com.izouma.awesomeadmin.dto.WebServiceResult;
import com.izouma.awesomeadmin.model.WebServiceRequest;
import com.izouma.awesomeadmin.util.CheckUtil;
import org.apache.axis.message.MessageElement;
import org.apache.axis.message.Text;
import org.apache.axis.types.UnsignedByte;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

@RequiresAuthentication
@Controller
@RequestMapping("/webService")
public class WebServiceController {
    static {
        ConvertUtils.register(new Converter() {
            @Override
            public <T> T convert(Class<T> aClass, Object o) {
                try {
                    Calendar c = Calendar.getInstance();
                    c.setTime(new Date(Long.valueOf(String.valueOf(o))));
                    return (T) c;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, java.util.Calendar.class);
    }

    @RequestMapping(value = "/call", method = RequestMethod.POST)
    @ResponseBody
    public WebServiceResult callWebService(@RequestBody WebServiceRequest request) {
        try {

            if (checkZouma()) {

                return new WebServiceResult(false, "系统未授权，请联系管理员");
            }


            String serviceName = request.getServiceName();
            String packageName = "com.izouma.webservice." + serviceName + ".";
            String methodName = request.getMethodName();
            List<Object> requestParams = request.getParams();

            Class locatorClass = Class.forName(packageName + serviceName.substring(0, 1).toUpperCase()
                    + serviceName.substring(1) + "Locator");
            Object locator = locatorClass.newInstance();
            Object service = locatorClass.getMethod("get" + serviceName + "Soap").invoke(locator);
            Class serviceClass = Class.forName(packageName + serviceName.substring(0, 1).toUpperCase()
                    + serviceName.substring(1) + "Soap_PortType");

            Method[] methods = serviceClass.getMethods();
            for (Method m : methods) {
                if (m.getName().equals(methodName)) {
                    List<Object> params = new ArrayList<>();
                    for (int i = 0; i < m.getParameterTypes().length; i++) {
                        switch (m.getParameterTypes()[i].getName()) {
                            case "java.lang.String":
                                params.add(String.valueOf(requestParams.get(i)));
                                break;
                            case "[Ljava.lang.String;":
                                List list = (List) requestParams.get(i);
                                String[] array = new String[list.size()];
                                for (int j = 0; j < list.size(); j++) {
                                    array[j] = String.valueOf(list.get(j));
                                }
                                params.add(array);
                                break;
                            case "org.apache.axis.types.UnsignedByte":
                                params.add(new UnsignedByte(String.valueOf(requestParams.get(i))));
                                break;
                            case "java.util.Calendar":
                                Calendar calendar = Calendar.getInstance();
                                if (requestParams.get(i) instanceof String) {
                                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                    Date date = dateFormat.parse((String) requestParams.get(i));
                                    date = changeTimeZone(date, TimeZone.getTimeZone("GMT"), TimeZone.getTimeZone("Asia/Shanghai"));
                                    calendar.setTime(date);
                                    params.add(calendar);
                                } else if (requestParams.get(i) instanceof Long) {
                                    Date date = new Date((Long) requestParams.get(i));
                                    date = changeTimeZone(date, TimeZone.getTimeZone("GMT"), TimeZone.getTimeZone("Asia/Shanghai"));
                                    calendar.setTime(date);
                                    params.add(calendar);
                                } else {
                                    params.add("");
                                }
                                break;
                            case "int":
                                params.add(Integer.valueOf(String.valueOf(requestParams.get(i))));
                                break;
                            case "float":
                                params.add(Float.valueOf(String.valueOf(requestParams.get(i))));
                                break;
                            case "java.math.BigDecimal":
                                if (StringUtils.isNotEmpty(String.valueOf(requestParams.get(i)))) {
                                    params.add(BigDecimal.valueOf(Double.valueOf(String.valueOf(requestParams.get(i)))));
                                } else {
                                    params.add(null);
                                }
                                break;
                            case "boolean":
                                params.add(Boolean.valueOf(String.valueOf(requestParams.get(i))));
                                break;
//                            case "com.izouma.webservice.DictType":
//                                params.add(DictType.fromValue(String.valueOf(requestParams.get(i))));
//                                break;
                            case "[B":
                                params.add(String.valueOf(requestParams.get(i)).getBytes());
                                break;
                            default:
                                if ("DictType".equals(m.getParameterTypes()[i].getName().substring(m.getParameterTypes()[i].getName().lastIndexOf(".") + 1))) {
                                    Class<?> dictTypeClass = Class.forName(m.getParameterTypes()[i].getName());
                                    Method method = dictTypeClass.getMethod("fromValue", String.class);
                                    params.add(method.invoke(null, String.valueOf(requestParams.get(i))));
                                } else if (requestParams.get(i) instanceof Map) {
                                    params.add(mapToObject((Map<String, Object>) requestParams.get(i), m.getParameterTypes()[i]));
                                } else {
                                    params.add(null);
                                }
                                break;
                        }
                    }
                    Object res = m.invoke(service, params.toArray());
                    String resClass = res.getClass().getName().substring(res.getClass().getName().lastIndexOf(".") + 1);
                    if ("RtnDataTableResponse".equals(resClass)) {
                        Object o = parseTableResponse(res);
                        return new WebServiceResult(true, o, getRtnMessage(res), getRtnCode(res));
                    } else {
                        return new WebServiceResult(true, res, getRtnMessage(res), getRtnCode(res));
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return new WebServiceResult(false, "失败");
    }

    public String getRtnMessage(Object obj) {
        Class<?> clazz = obj.getClass();
        try {
            Method m = clazz.getMethod("getRtnMessage");
            String str = (String) m.invoke(obj);
            return str;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Integer getRtnCode(Object obj) {
        Class<?> clazz = obj.getClass();
        try {
            Method m = clazz.getMethod("getRtnCode");
            int code = (int) m.invoke(obj);
            return code;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception {
        if (map == null)
            return null;

        Object obj = beanClass.newInstance();

        org.apache.commons.beanutils.BeanUtils.populate(obj, map);

        return obj;
    }

    public static Map<?, ?> objectToMap(Object obj) {
        if (obj == null)
            return null;

        return new org.apache.commons.beanutils.BeanMap(obj);
    }

    public MessageElement[] getMessageElements(Object object) {
        try {
            Class<?> c1 = object.getClass();
            Method getRtnDataTable = c1.getMethod("getRtnDataTable");
            Object rtnDataTable = getRtnDataTable.invoke(object);
            Class<?> c2 = rtnDataTable.getClass();
            Method getAny = c2.getMethod("get_any");
            MessageElement[] messageElements = (MessageElement[]) getAny.invoke(rtnDataTable);
            return messageElements;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Object parseTableResponse(Object response) {
        MessageElement[] messageElements = getMessageElements(response);
        if (messageElements != null) {
            for (MessageElement messageElement : messageElements) {
                if (messageElement.getName().equals("diffgram")) {
                    return parseMessage(messageElement);
                }
            }
        }
        return null;
    }

    public Object parseMessage(MessageElement message) {
        if (message.getChildren() == null) {
            KeyValue kv = new KeyValue();
            kv.setKey(message.getName());
            kv.setValue("");
            return kv;
        }
        if (message.getChildren().size() > 1) {
            boolean isMap = true;
            List<Object> list = new ArrayList<>();
            for (int i = 0; i < message.getChildren().size(); i++) {
                Object obj = parseMessage((MessageElement) message.getChildren().get(i));
                list.add(obj);
                if (!(obj instanceof KeyValue)) {
                    isMap = false;
                }
            }
            if (isMap) {
                Map<String, Object> map = new HashMap<>();
                for (Object o : list) {
                    map.put(((KeyValue) o).getKey(), ((KeyValue) o).getValue());
                }
                return map;
            }
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i) instanceof KeyValue) {
                    Map<String, Object> kvmap = new HashMap<>();
                    kvmap.put(((KeyValue) list.get(i)).getKey(), ((KeyValue) list.get(i)).getValue());
                    list.set(i, kvmap);
                }
            }
            return list;
        } else if (message.getChildren().size() == 1) {
            if (message.getChildren().get(0) instanceof MessageElement) {
                Object child = parseMessage((MessageElement) message.getChildren().get(0));
                if ("Table".equals(message.getName()) && child instanceof KeyValue) {
                    Map<String, Object> map = new HashMap<>();
                    map.put(((KeyValue) child).getKey(), ((KeyValue) child).getValue());
                    return map;
                } else {
                    return child;
                }
            } else if (message.getChildren().get(0) instanceof Text) {
                KeyValue kv = new KeyValue();
                kv.setKey(message.getName());
                kv.setValue(((Text) message.getChildren().get(0)).getData());
                return kv;
            }
        }
        return null;
    }

    public class KeyValue {
        private String key;
        private Object value;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }
    }

    private Date changeTimeZone(Date date, TimeZone oldZone, TimeZone newZone) {
        Date dateTmp = null;
        if (date != null) {
            int timeOffset = oldZone.getRawOffset() - newZone.getRawOffset();
            dateTmp = new Date(date.getTime() - timeOffset);
        }
        return dateTmp;
    }

    private boolean checkZouma() {

        return !CheckUtil.checkProjectActive();

    }
}
