package com.ecep.contract.handler;

import com.ecep.contract.*;
import com.ecep.contract.constant.WebSocketConstant;
import com.ecep.contract.ds.other.service.EmployeeLoginHistoryService;
import com.ecep.contract.ds.other.service.EmployeeService;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.EmployeeLoginHistory;
import com.ecep.contract.model.Voable;
import com.ecep.contract.service.WebSocketServerTaskManager;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;

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.nio.ByteBuffer;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * WebSocket处理器
 * 处理与客户端的WebSocket连接、消息传递和断开连接
 */
@Component
public class WebSocketServerHandler extends TextWebSocketHandler {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketServerHandler.class);

    private final AuthenticationManager authenticationManager;
    private final ObjectMapper objectMapper;

    @Autowired
    private EmployeeLoginHistoryService employeeLoginHistoryService;
    @Autowired
    private EmployeeService employeeService;
    @Autowired
    private ScheduledExecutorService scheduledExecutorService;
    @Autowired
    private WebSocketServerTaskManager taskManager;

    // 存储所有活跃的WebSocket会话
    private final Map<Integer, SessionInfo> activeSessions = Collections.synchronizedMap(new HashMap<>());

    @Data
    static class SessionInfo {
        private Integer employeeId;
        private Integer loginHistoryId;
        private WebSocketSession session;
        private ScheduledFuture<?> pingPongScheduledFuture;

        void click() {
            try {
                session.sendMessage(new PingMessage(ByteBuffer.wrap("ping".getBytes())));
            } catch (IOException e) {
                logger.error("发送ping消息失败 (会话ID: " + session.getId() + "): " + e.getMessage(), e);
            }
        }
    }

    WebSocketServerHandler(ObjectMapper objectMapper, AuthenticationManager authenticationManager) {
        this.objectMapper = objectMapper;
        this.authenticationManager = authenticationManager;
    }

    /**
     * 连接建立时调用
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 添加会话到活跃会话集合
        SessionInfo sessionInfo = new SessionInfo();
        sessionInfo.setSession(session);
        sessionInfo.setLoginHistoryId((Integer) session.getAttributes().get("loginHistoryId"));
        sessionInfo.setEmployeeId((Integer) session.getAttributes().get("employeeId"));

        if (sessionInfo.getEmployeeId() == null) {
            logger.error("会话未绑定用户: {}", session.getId());
            sendError(session, WebSocketConstant.ERROR_CODE_UNAUTHORIZED, "会话未绑定用户");
            session.close(CloseStatus.NOT_ACCEPTABLE);
            return;
        }

        activeSessions.put(sessionInfo.getEmployeeId(), sessionInfo);
        logger.info("WebSocket连接已建立: {}", session.getId());

        Employee employee = employeeService.findById(sessionInfo.getEmployeeId());
        if (employee == null) {
            logger.error("未找到用户: #{}", sessionInfo.getEmployeeId());
            return;
        }

        ScheduledFuture<?> schedule = scheduledExecutorService.schedule(sessionInfo::click, 30, TimeUnit.SECONDS);
        sessionInfo.setPingPongScheduledFuture(schedule);
    }

    /**
     * 接收文本消息时调用
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        if (logger.isInfoEnabled()) {
            logger.info("收到来自客户端的消息: {} (会话ID: {})", payload, session.getId());
        }
        // 处理文本格式的ping消息
        if ("ping".equals(payload)) {
            // 回复文本格式的pong消息
            try {
                session.sendMessage(new TextMessage("pong"));
                logger.info("发送pong响应到会话: " + session.getId());
            } catch (IOException e) {
                logger.error("发送pong响应失败 (会话ID: " + session.getId() + "): " + e.getMessage(), e);
            }
            return;
        }

        // 尝试将消息作为JSON处理
        if (handleAsJson(session, payload)) {
            return;
        }

        // 处理其他类型的文本消息
        logger.info("处理普通消息: " + payload);
    }

    private boolean handleAsJson(WebSocketSession session, String payload) {
        if (!session.isOpen()) {
            logger.warn("尝试在已关闭的WebSocket会话上处理消息回调");
            return true;
        }

        JsonNode jsonNode = null;
        try {
            jsonNode = objectMapper.readTree(payload);
        } catch (Exception e) {
            logger.warn("解析消息回调JSON失败: {}", payload, e);
            return false;
        }

        if (jsonNode.has(WebSocketConstant.MESSAGE_ID_FIELD_NAME)) {
            // 处理 messageId 的消息
            String messageId = jsonNode.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME).asText();
            try {
                handleAsMessageCallback(session, messageId, jsonNode);
            } catch (Exception e) {
                sendError(session, messageId, e.getMessage());
                logger.warn("处理消息回调失败 (消息ID: {}): {}", messageId, e.getMessage(), e);
            }
            return true;
        }
        if (jsonNode.has(WebSocketConstant.SESSION_ID_FIELD_NAME)) {
            taskManager.onMessage(session, jsonNode);
            return true;
        }
        return false;
    }

    private void handleAsMessageCallback(WebSocketSession 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);
        }

        Map<String, Object> map = new HashMap<>();
        if (result instanceof Voable<?>) {
            map.put("data", ((Voable<?>) result).toVo());
        } else {
            map.put("data", result);
        }
        map.put(WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId);
        map.put(WebSocketConstant.SUCCESS_FIELD_VALUE, true);

        String response = objectMapper.writeValueAsString(map);
        session.sendMessage(new TextMessage(response));

    }

    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.findById(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()) {
                int id = paramsNode.get("id").asInt();
                entity = entityService.findById(id);
                if (entity == null) {
                    throw new NoSuchElementException("未找到实体: #" + id);
                }
            } else {
                entity = createNewEntity(entityService);
                if (entity == null) {
                    throw new UnsupportedOperationException("无法创建实体对象, " + service.getClass().getName());
                }
            }
            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.findById(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);
        QueryService<?> entityService = (QueryService<?>) service;
        Page<?> page = entityService.findAll(paramsNode, pageArgument.toPageable());
        return PageContent.of(page.map(entity -> {
            if (entity instanceof Voable<?>) {
                return ((Voable<?>) entity).toVo();
            }
            return entity;
        }));
    }

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

    private void sendError(WebSocketSession session, String messageId, String message) {
        _sendError(session, WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId, message);
    }

    private void _sendError(WebSocketSession session, String fieldName, String messageId, String message) {
        if (session == null || !session.isOpen()) {
            logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
            return;
        }

        try {
            String errorMessage = objectMapper.writeValueAsString(Map.of(
                    fieldName, messageId,
                    WebSocketConstant.SUCCESS_FIELD_VALUE, false,
                    WebSocketConstant.MESSAGE_FIELD_NAME, message));

            // 检查会话状态并尝试发送错误消息
            if (session.isOpen()) {
                session.sendMessage(new TextMessage(errorMessage));
            } else {
                logger.warn("会话已关闭，无法发送错误消息: {}", message);
            }
        } catch (Exception e) {
            // 捕获所有可能的异常，防止影响主流程
            logger.error("发送错误消息失败 (会话ID: {})", session.getId(), e);
        }
    }

    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
        logger.info("收到来自客户端的二进制消息: " + message.getPayload() + " (会话ID: " + session.getId() + ")");
        // 处理二进制消息
        // 例如，将字节数组转换为字符串
        String binaryData = new String(message.getPayload().array());
        logger.info("二进制消息内容: " + binaryData);
    }

    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
        logger.info("收到来自客户端的Pong消息: " + message.getPayload() + " (会话ID: " + session.getId() + ")");

        // 从活跃会话集合中移除会话
        SessionInfo sessionInfo = activeSessions.get((Integer) session.getAttributes().get("employeeId"));
        if (sessionInfo == null) {
            logger.warn("收到来自客户端的Pong消息，但会话不存在: " + session.getId());
            return;
        }
        Integer loginHistoryId = sessionInfo.getLoginHistoryId();
        if (loginHistoryId != null) {
            EmployeeLoginHistory history = employeeLoginHistoryService.findById(loginHistoryId);
            history.setActiveTime(LocalDateTime.now());
            employeeLoginHistoryService.save(history);
        }
    }

    /**
     * 连接关闭时调用
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

        logger.info(
                "WebSocket连接已关闭: " + session.getId() + ", 状态码: " + status.getCode() + ", 原因: " + status.getReason());
        // 从活跃会话集合中移除会话
        SessionInfo sessionInfo = activeSessions.remove((Integer) session.getAttributes().get("employeeId"));
        if (sessionInfo == null) {
            return;
        }
        ScheduledFuture<?> schedule = sessionInfo.getPingPongScheduledFuture();
        if (schedule != null) {
            schedule.cancel(true);
            sessionInfo.setPingPongScheduledFuture(null);
        }

        Integer loginHistoryId = sessionInfo.getLoginHistoryId();
        if (loginHistoryId != null) {
            EmployeeLoginHistory history = employeeLoginHistoryService.findById(loginHistoryId);
            history.setActiveTime(LocalDateTime.now());
            employeeLoginHistoryService.save(history);
        }

    }

    /**
     * 处理传输错误
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        logger.error("WebSocket传输错误 (会话ID: " + session.getId() + "): " + exception.getMessage(), exception);
        // 连接出错时关闭会话
        if (session.isOpen()) {
            session.close(CloseStatus.SERVER_ERROR);
        }
    }

    /**
     * 向所有连接的客户端广播消息
     */
    public void broadcast(TextMessage message) {
        synchronized (activeSessions) {
            for (SessionInfo sessionInfo : activeSessions.values()) {
                WebSocketSession session = sessionInfo.getSession();
                if (session.isOpen()) {
                    try {
                        session.sendMessage(message);
                        logger.info("广播消息到会话: " + session.getId());
                    } catch (IOException e) {
                        logger.error("广播消息失败 (会话ID: " + session.getId() + "): " + e.getMessage(), e);
                    }
                }
            }
        }
    }

    /**
     * 获取当前活跃会话数
     */
    public int getActiveSessionCount() {
        return activeSessions.size();
    }

    private void sendError(WebSocketSession session, int errorCode, String message) {
        if (session == null || !session.isOpen()) {
            logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
            return;
        }

        try {
            ObjectNode objectNode = objectMapper.createObjectNode();
            objectNode.put(WebSocketConstant.ERROR_CODE_FIELD_NAME, errorCode);
            objectNode.put(WebSocketConstant.SUCCESS_FIELD_VALUE, false);
            objectNode.put(WebSocketConstant.MESSAGE_FIELD_NAME, message);
            String errorMessage = objectMapper.writeValueAsString(objectNode);

            // 检查会话状态并尝试发送错误消息
            if (session.isOpen()) {
                session.sendMessage(new TextMessage(errorMessage));
            } else {
                logger.warn("会话已关闭，无法发送错误消息: {}", message);
            }
        } catch (Exception e) {
            // 捕获所有可能的异常，防止影响主流程
            logger.error("发送错误消息失败 (会话ID: {})", session.getId(), e);
        }
    }
}