package com.ecep.contract.service;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import com.ecep.contract.IEntityService;
import com.ecep.contract.PageArgument;
import com.ecep.contract.PageContent;
import com.ecep.contract.QueryService;
import com.ecep.contract.SpringApp;
import com.ecep.contract.constant.WebSocketConstant;
import com.ecep.contract.handler.SessionInfo;
import com.ecep.contract.model.Voable;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

@Service
public class WebSocketServerCallbackManager {
    static final Logger logger = LoggerFactory.getLogger(WebSocketServerCallbackManager.class);
    private final ObjectMapper objectMapper;

    public WebSocketServerCallbackManager(@Autowired ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    public void onMessage(SessionInfo session, JsonNode jsonNode) {
        // 处理 messageId 的消息
        String messageId = jsonNode.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME).asText();
        try {
            Object result = handleAsMessageCallback(session, messageId, jsonNode);
            send(session, messageId, result);
        } catch (Exception e) {
            sendError(session, messageId, 500, e.getMessage());
            logger.warn("处理消息回调失败 (消息ID: {}): {}", messageId, e.getMessage(), e);
        }
    }

    private void sendError(SessionInfo session, String messageId, int errorCode, String message) {
        try {
            session.sendError(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId, errorCode, message);
        } catch (IOException e) {
            logger.warn("发送错误消息失败 (消息ID: {}): {}", messageId, e.getMessage(), e);
        }
    }

    private void send(SessionInfo session, String messageId, Object data) {
        Map<String, Object> map = new HashMap<>();
        if (data instanceof Voable<?>) {
            map.put("data", ((Voable<?>) data).toVo());
        } else {
            map.put("data", data);
        }
        map.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId);
        map.put(WebSocketConstant.SUCCESS_FIELD_NAME, true);
        try {
            session.send(map);
        } catch (IOException e) {
            logger.warn("发送消息失败 (消息ID: {}): {}", messageId, e.getMessage(), e);
        }
    }

    private Object handleAsMessageCallback(SessionInfo session, String messageId, JsonNode jsonNode)
            throws Exception {
        if (!jsonNode.has(WebSocketConstant.SERVICE_FIELD_NAME)) {
            throw new IllegalArgumentException("缺失 service 参数");
        }

        String serviceName = jsonNode.get(WebSocketConstant.SERVICE_FIELD_NAME).asText();
        Object service = null;
        try {
            service = SpringApp.getBean(serviceName);
        } catch (Exception e) {
            throw new IllegalArgumentException("未找到服务: " + serviceName);
        }

        if (!jsonNode.has(WebSocketConstant.METHOD_FIELD_NAME)) {
            throw new IllegalArgumentException("缺失 method 参数");
        }

        String methodName = jsonNode.get(WebSocketConstant.METHOD_FIELD_NAME).asText();
        JsonNode argumentsNode = jsonNode.get(WebSocketConstant.ARGUMENTS_FIELD_NAME);

        Object result = null;
        if (methodName.equals("findAll")) {
            result = invokerFindAllMethod(service, argumentsNode);
        } else if (methodName.equals("findById")) {
            result = invokerFindByIdMethod(service, argumentsNode);
        } else if (methodName.equals("save")) {
            result = invokerSaveMethod(service, argumentsNode);
        } else if (methodName.equals("delete")) {
            result = invokerDeleteMethod(service, argumentsNode);
        } else if (methodName.equals("count")) {
            result = invokerCountMethod(service, argumentsNode);
        } else {
            result = invokerOtherMethod(service, methodName, argumentsNode);
        }
        return result;
    }

    private Object invokerOtherMethod(Object service, String methodName, JsonNode argumentsNode)
            throws NoSuchMethodException, SecurityException, InvocationTargetException, IllegalAccessException,
            ClassNotFoundException, JsonProcessingException {
        int size = argumentsNode.size();
        Class<?> targetClass = getTargetClass(service.getClass());
        if (size == 0) {
            Method method = targetClass.getMethod(methodName);
            return method.invoke(service);
        }

        if (!argumentsNode.get(0).isArray()) {
            Class<?> parameterType = Class.forName(argumentsNode.get(1).asText());
            Object arg = objectMapper.treeToValue(argumentsNode.get(0), parameterType);
            Method method = targetClass.getMethod(methodName, parameterType);
            return method.invoke(service, arg);
        }

        // 参数值
        JsonNode paramsNode = argumentsNode.get(0);
        // 参数类型
        JsonNode typesNode = argumentsNode.get(1);

        Class<?>[] parameterTypes = new Class<?>[typesNode.size()];
        Object[] args = new Object[paramsNode.size()];
        for (int i = 0; i < typesNode.size(); i++) {
            String type = typesNode.get(i).asText();
            parameterTypes[i] = Class.forName(type);
            args[i] = objectMapper.treeToValue(paramsNode.get(i), parameterTypes[i]);
        }

        try {
            Method method = targetClass.getMethod(methodName, parameterTypes);
            return method.invoke(service, args);
        } catch (NoSuchMethodException e) {
            logger.error("NoSuchMethodException, targetClass: {}, Methods:{}", targetClass, targetClass.getMethods());
            throw e;
        }
    }

    private Object invokerDeleteMethod(Object service, JsonNode argumentsNode) {
        JsonNode paramsNode = argumentsNode.get(0);
        if (!paramsNode.has("id")) {
            throw new IllegalArgumentException("缺失 id 参数");
        }
        int id = paramsNode.get("id").asInt();
        IEntityService<Object> entityService = (IEntityService<Object>) service;
        Object entity = entityService.getById(id);
        if (entity == null) {
            throw new NoSuchElementException("未找到实体: #" + id);
        }
        entityService.delete(entity);
        return entity;
    }

    private Object invokerSaveMethod(Object service, JsonNode argumentsNode) throws JsonMappingException {
        JsonNode paramsNode = argumentsNode.get(0);
        if (service instanceof IEntityService<?> entityService) {
            Object entity = null;

            if (paramsNode.has("id") && (!paramsNode.get("id").isNull() && paramsNode.get("id").asInt() > 0)) {
                int id = paramsNode.get("id").asInt();
                entity = entityService.getById(id);
                if (entity == null) {
                    throw new NoSuchElementException("未找到实体: #" + id);
                }
            } else {
                entity = createNewEntity(entityService);
            }

            if (service instanceof VoableService<?, ?>) {
                String typeClz = argumentsNode.get(1).asText();
                Class<?> type = null;
                try {
                    type = Class.forName(typeClz);
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
                Object object = objectMapper.convertValue(paramsNode, type);
                ((VoableService<Object, Object>) service).updateByVo(entity, object);
            } else {
                objectMapper.updateValue(entity, paramsNode);
            }
            return ((IEntityService<Object>) entityService).save(entity);
        }

        return null;
    }

    private <T> T createNewEntity(IEntityService<T> entityService) {
        try {
            // 通过分析接口的泛型参数来获取实体类型
            Class<?> serviceClass = entityService.getClass();

            // 1. 直接检查接口
            Class<T> entityClass = findEntityTypeInInterfaces(serviceClass);
            if (entityClass != null) {
                return entityClass.getDeclaredConstructor().newInstance();
            }

            // 2. 处理Spring代理类 - 获取原始类
            Class<?> targetClass = getTargetClass(serviceClass);
            if (targetClass != serviceClass) {
                entityClass = findEntityTypeInInterfaces(targetClass);
                if (entityClass != null) {
                    return entityClass.getDeclaredConstructor().newInstance();
                }
            }

            // 3. 尝试查找父类
            entityClass = findEntityTypeInSuperclass(serviceClass);
            if (entityClass != null) {
                return entityClass.getDeclaredConstructor().newInstance();
            }

            // 4. 如果上述方法都失败，尝试从参数类型推断
            entityClass = findEntityTypeFromMethodParameters(serviceClass);
            if (entityClass != null) {
                return entityClass.getDeclaredConstructor().newInstance();
            }

            // 如果所有方法都失败，抛出更具描述性的异常
            throw new UnsupportedOperationException("无法确定实体类型，请检查服务实现: " + serviceClass.getName());
        } catch (Exception e) {
            throw new RuntimeException("无法创建Entity实例: " + e.getMessage(), e);
        }
    }

    /**
     * 从接口中查找实体类型
     */
    @SuppressWarnings("unchecked")
    private <T> Class<T> findEntityTypeInInterfaces(Class<?> serviceClass) {
        Type[] interfaces = serviceClass.getGenericInterfaces();

        for (Type iface : interfaces) {
            if (iface instanceof ParameterizedType paramType) {
                if (IEntityService.class.isAssignableFrom((Class<?>) paramType.getRawType())) {
                    // 获取IEntityService的泛型参数类型
                    Type entityType = paramType.getActualTypeArguments()[0];
                    if (entityType instanceof Class<?>) {
                        return (Class<T>) entityType;
                    } else if (entityType instanceof ParameterizedType) {
                        // 处理参数化类型
                        Type rawType = ((ParameterizedType) entityType).getRawType();
                        if (rawType instanceof Class<?>) {
                            return (Class<T>) rawType;
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * 从父类中查找实体类型
     */
    @SuppressWarnings("unchecked")
    private <T> Class<T> findEntityTypeInSuperclass(Class<?> serviceClass) {
        Type genericSuperclass = serviceClass.getGenericSuperclass();
        while (genericSuperclass != null && genericSuperclass != Object.class) {
            if (genericSuperclass instanceof ParameterizedType paramType) {
                Type rawType = paramType.getRawType();
                if (rawType instanceof Class<?> && IEntityService.class.isAssignableFrom((Class<?>) rawType)) {
                    Type entityType = paramType.getActualTypeArguments()[0];
                    if (entityType instanceof Class<?>) {
                        return (Class<T>) entityType;
                    }
                }
            }

            // 继续查找父类的父类
            if (genericSuperclass instanceof Class<?>) {
                genericSuperclass = ((Class<?>) genericSuperclass).getGenericSuperclass();
            } else {
                break;
            }
        }
        return null;
    }

    /**
     * 尝试从方法参数类型推断实体类型
     */
    @SuppressWarnings("unchecked")
    private <T> Class<T> findEntityTypeFromMethodParameters(Class<?> serviceClass) {
        try {
            // 尝试通过findById方法推断实体类型
            Method findByIdMethod = serviceClass.getMethod("findById", Integer.class);
            if (findByIdMethod != null) {
                return (Class<T>) findByIdMethod.getReturnType();
            }

            // 尝试通过findAll方法推断实体类型
            Method[] methods = serviceClass.getMethods();
            for (Method method : methods) {
                if (method.getName().equals("findAll") && method.getParameterCount() > 0) {
                    Type returnType = method.getGenericReturnType();
                    if (returnType instanceof ParameterizedType paramType &&
                            paramType.getRawType() instanceof Class<?> &&
                            "org.springframework.data.domain.Page"
                                    .equals(((Class<?>) paramType.getRawType()).getName())) {

                        Type[] actualTypeArguments = paramType.getActualTypeArguments();
                        if (actualTypeArguments.length > 0 && actualTypeArguments[0] instanceof Class<?>) {
                            return (Class<T>) actualTypeArguments[0];
                        }
                    }
                }
            }
        } catch (Exception e) {
            // 忽略异常，继续尝试其他方法
        }
        return null;
    }

    /**
     * 获取被代理的原始类
     */
    private Class<?> getTargetClass(Class<?> proxyClass) {
        // 处理CGLIB代理类
        if (proxyClass.getName().contains("$$SpringCGLIB$$")) {
            return proxyClass.getSuperclass();
        }

        return proxyClass;
    }

    /*
     * see client QueryService#findById(Integer)
     */
    private Object invokerFindByIdMethod(Object service, JsonNode argumentsNode) {
        JsonNode paramsNode = argumentsNode.get(0);
        if (service instanceof IEntityService<?> entityService) {
            Integer id = paramsNode.asInt();
            return entityService.getById(id);
        }

        try {
            JsonNode typesNode = argumentsNode.get(1);
            if (paramsNode.isInt()) {
                Method method = service.getClass().getMethod("findById", Integer.class);
                return method.invoke(service, paramsNode.asInt());
            }
            if (paramsNode.isTextual()) {
                Method method = service.getClass().getMethod("findById", String.class);
                return method.invoke(service, paramsNode.asText());
            }
            throw new IllegalArgumentException("unable to invoke findById method, paramsNode is not int or text");
        } catch (Exception e) {
            throw new RuntimeException("unable to invoke findById method", e);
        }
    }

    private Object invokerFindAllMethod(Object service, JsonNode argumentsNode) throws JsonProcessingException {
        JsonNode paramsNode = argumentsNode.get(0);
        JsonNode pageableNode = argumentsNode.get(1);
        PageArgument pageArgument = objectMapper.treeToValue(pageableNode, PageArgument.class);
        if (service instanceof QueryService<?> queryService) {
            Page<?> page = queryService.findAll(paramsNode, pageArgument.toPageable());
            return PageContent.of(page.map(entity -> {
                if (entity instanceof Voable<?>) {
                    return ((Voable<?>) entity).toVo();
                }
                return entity;
            }));
        }
        throw new RuntimeException(service + " should be QueryService");
    }

    private Object invokerCountMethod(Object service, JsonNode argumentsNode) {
        JsonNode paramsNode = argumentsNode.get(0);
        if (service instanceof QueryService<?> entityService) {
            return entityService.count(paramsNode);
        }
        return null;
    }
}
