package com.storlead.framework.web.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.storlead.framework.auth.vo.LoginUser;
import com.storlead.framework.common.constant.DefContants;
import com.storlead.framework.common.constant.UserCacheKeyConstants;
import com.storlead.framework.common.util.SystemUtils;
import com.storlead.framework.common.util.UrlChainBlackAndWhiteUtil;
import com.storlead.framework.core.context.Context;
import com.storlead.framework.core.context.IContext;
import com.storlead.framework.core.tenant.TenantConstants;
import com.storlead.framework.core.tenant.TenantContext;
import com.storlead.framework.redis.RedisService;
import com.storlead.framework.common.result.Result;
import com.storlead.framework.common.enums.ResultCode;
import com.storlead.framework.web.wrapper.JsonRequestWrapper;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @program: storlead-storlead-mail-platform
 * @description:
 * @author: chenkq
 * @create: 2025-06-17 15:48
 */
@Log4j2
@Component
public class AuthRequestFilter implements Filter, Ordered {

    @Resource
    private RedisService redisService;

    @Resource
    private UrlChainBlackAndWhiteUtil chainBlackAndWhiteUtil;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        IContext context = Context.getContext();
        if (context == null) {
            Context ct = new Context();
            ct.init(request, response);
            Context.setContext(ct);
        }
        String contentType = request.getContentType();
        if (StringUtils.indexOfIgnoreCase(contentType, "application/json") >= 0 ) {
            if (contentType.contains("application/json")) {
                request = jsonToParameter(request);
            }
        } else {
            if (request.getMethod().equals("GET")) {
                request = jsonToParameter(request);
            }
        }
        Boolean islogin = loginHandle(request);
        if (!islogin) {
            Result result1 = Result.error(ResultCode.LOGIN_FAIL);
            response.setStatus(HttpStatus.OK.value());
            SystemUtils.printJson(response, JSON.toJSONString(result1));
            return;
        }
        try {
            if (request.getRequestURI().equals("/api/router/rest")) {
                String code = request.getHeader("method");
                if (StrUtil.isBlank(code)) {
                    Result result1 = Result.error("missing 'method' header");
                    response.setStatus(HttpStatus.OK.value());
                    SystemUtils.printJson(response, JSON.toJSONString(result1));
                    return;
                }

                String targetPath = CodeRouteMapping.getTargetPath(code);
                RequestDispatcher dispatcher = request.getRequestDispatcher(targetPath);
                dispatcher.forward(request, response);
            } else {
                chain.doFilter(request, response);
            }
        } finally {
            if (context != null) {
                context.invalidate();
            }
            context = null;
        }
    }

    public void getAllParameters(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();

        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String paramName = entry.getKey();
            String[] paramValues = entry.getValue();
        }
    }

    private Boolean loginHandle(HttpServletRequest req) {
        // 获取URL
        String url = req.getRequestURI();
        log.error("login--WebSocket ---- url---"+url);
        Boolean isNeedLogin = chainBlackAndWhiteUtil.IsWhiteUri(url);
        if (isNeedLogin) {
            return isNeedLogin;
        }
        log.error("login--WebSocket ---- isNeedLogin---"+url);
        String token = req.getHeader(DefContants.ACCESS_TOKEN);
        if (StrUtil.isBlank(token)) {
            token = req.getHeader("Sec-WebSocket-Protocol");
            if (StrUtil.isNotBlank(token)) {
                log.error("login--WebSocket ---- token---"+token);
                req.setAttribute("token",token);
            }
        }

        if (StringUtils.isNotBlank(token)) {
            String jsonInfo = redisService.getCacheObject(token);
            if (StrUtil.isNotBlank(jsonInfo) && jsonInfo.length() < "1efb52db4da5d2b92516a4eba0b3145b34234234".length()) {
                jsonInfo = redisService.getCacheObject(jsonInfo);
            }
            if (StringUtils.isNotBlank(jsonInfo)) {
                try {
                    IContext context = Context.getContext();
                    LoginUser loginUserInfo = JSONUtil.toBean(jsonInfo, LoginUser.class);

                    String  scopeMenuId =  context.getAttribute("scopeMenuId",String.class);
                    Object  commonScope =  context.getAttribute("commonScope",Object.class);
                    Object  commonScopeType =  context.getAttribute("commonScopeType",Object.class);
                    // 当前接口的地址
                    loginUserInfo.setApiUrl(url);
                    loginUserInfo.setScopeMenuId(scopeMenuId);
                    if (Objects.nonNull(commonScope)) {
                        loginUserInfo.setCommonScope((Integer)commonScope);
                    }
                    if (Objects.nonNull(commonScopeType)) {
                        loginUserInfo.setCommonScopeType(commonScopeType.toString());
                    }
                    context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_KEY, loginUserInfo);
                    context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_TOKEN_KEY, token);
                    context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_ID_KEY, loginUserInfo.getId());

                    Long tenantId = loginUserInfo.getTenantId();
                    if (tenantId == null) {
                        tenantId = loginUserInfo.getCompanyId();
                    }
                    if (tenantId != null) {
                        TenantContext.bindTenantId(tenantId);
                    }
                    String headerTenant = req.getHeader(TenantConstants.HTTP_HEADER_TENANT_ID);
                    if (StringUtils.isNotBlank(headerTenant)) {
                        try {
                            Long headerTid = Long.valueOf(headerTenant.trim());
                            if (tenantId != null && !tenantId.equals(headerTid)) {
                                log.warn("X-Tenant-Id {} 与登录租户 {} 不一致，已以登录信息为准", headerTid, tenantId);
                            } else if (tenantId == null && Boolean.TRUE.equals(loginUserInfo.getIsAdmin())) {
                                TenantContext.bindTenantId(headerTid);
                            }
                        } catch (NumberFormatException e) {
                            log.warn("非法 X-Tenant-Id 请求头: {}", headerTenant);
                        }
                    }
                    if (TenantContext.getTenantId() == null) {
                        log.warn("已登录用户未解析到租户 ID（LoginUser.tenantId / companyId 均为空），多租户条件将不生效: userId={}", loginUserInfo.getId());
                    }

                    // 修改失效时间,超过24小时没有使用,
                    Long expire = redisService.getCacheExpire(token,TimeUnit.SECONDS);
                    log.debug("getCacheExpire-----------当前用户信息 = "+loginUserInfo);
                    if (expire < 3600) {
                        redisService.setCacheExpire(token,60 * 60 * 24L * 7, TimeUnit.SECONDS);
                    }
                } catch (Exception e) {
                    log.error("loginHandle error",e);
                    return false;
                }
            } else {
                return false;
            }
        } else {
            return false;
        }
        return true;
    }

    @Override
    public void destroy() {

    }

    @Override
    public int getOrder() {
        return -90;
    }


    public HttpServletRequest jsonToParameter(HttpServletRequest req) {
        String paramJson =  "";
        String method = req.getMethod();
        Map<String, Object> jsonRequestParam = new HashMap<>();
        List<Object> array = new ArrayList<>();
        if ("POST".equalsIgnoreCase(method)) {
            paramJson = requestInputStream(req);
            if (StringUtils.isNotBlank(paramJson)) {
                final ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
                try {
                    JsonNode root = objectMapper.readTree(paramJson);
                    if (root instanceof ObjectNode) {
                        Iterator<Map.Entry<String, JsonNode>> elements = root.fields();
                        while (elements.hasNext()) {
                            Map.Entry<String, JsonNode> node = elements.next();
                            String key = node.getKey();
                            Object val = objectMapper.readValue(node.getValue().toString(), Object.class);
                            jsonRequestParam.put(key, val);
                            if (key.equals("scopeMenuId")) {
                                IContext context = Context.getContext();
                                context.setAttribute("scopeMenuId",val);
                            }
                            if (key.equals("commonScope")) {
                                IContext context = Context.getContext();
                                context.setAttribute("commonScope",val);
                            }
                            if (key.equals("commonScopeType")) {
                                IContext context = Context.getContext();
                                context.setAttribute("commonScopeType",val);
                            }
                        }
                    }   else  if (root instanceof ArrayNode) {
                        for (JsonNode r: root) {
                            Iterator<Map.Entry<String, JsonNode>> elements = r.fields();
                            Map<String, Object> jsonRequestListParam = new HashMap<>();
                            while (elements.hasNext()) {
                                Map.Entry<String, JsonNode> node = elements.next();
                                String key = node.getKey();
                                Object val = objectMapper.readValue(node.getValue().toString(), Object.class);
                                jsonRequestListParam.put(key, val);
                                if (key.equals("scopeMenuId")) {
                                    IContext context = Context.getContext();
                                    context.setAttribute("scopeMenuId",val);
                                }
                                if (key.equals("commonScope")) {
                                    IContext context = Context.getContext();
                                    context.setAttribute("commonScope",Integer.valueOf(val.toString()));
                                }
                                if (key.equals("commonScopeType")) {
                                    IContext context = Context.getContext();
                                    context.setAttribute("commonScopeType",val.toString());
                                }
                            }
                            array.add(jsonRequestListParam);
                        }
                    }
                } catch (Exception e) {
                    log.error("jsonToParameter -- error -- ",e);
                }
                //log.error("jsonToParameter ------------array ="+array);
                //将请求参数存储到上下文中(日志打印)
                if (!CollectionUtils.isEmpty(array)) {
                    Context.getContext().setAttribute("httpJsonRequestListParam", array);
                    JsonRequestWrapper jRequestWrapper = new JsonRequestWrapper(req);
                    return jRequestWrapper;
                }
                if (StringUtils.isNotBlank(paramJson)) {
                    Context.getContext().setAttribute("httpJsonRequestParam", jsonRequestParam);
                    JsonRequestWrapper jRequestWrapper = new JsonRequestWrapper(req);
                    return jRequestWrapper;
                }

            }
        } else if ("GET".equalsIgnoreCase(method)) {
            Map<String, String[]> getParams = req.getParameterMap();
            // 获取所有 GET 参数
            if (!getParams.isEmpty()) {
                try {
                    Map<String, Object> jsonParams = new HashMap<>();
                    if (!getParams.isEmpty()) {
                        getParams.forEach((key, values) -> {
                            Object val;
                            if (values.length == 1) {
                                jsonParams.put(key, values[0]);
                                val = values[0];// 单值参数
                            } else {
                                jsonParams.put(key, values);
                                val = values;// 多值参数（如复选框）
                            }

                            if (key.equals("scopeMenuId")) {
                                IContext context = Context.getContext();
                                context.setAttribute("scopeMenuId",val);
                            }
                            if (key.equals("commonScope")) {
                                IContext context = Context.getContext();
                                context.setAttribute("commonScope",val);
                            }
                            if (key.equals("commonScopeType")) {
                                IContext context = Context.getContext();
                                context.setAttribute("commonScopeType",val);
                            }
                        });
                    }
                    final ObjectMapper objectMapper = new ObjectMapper();
                    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                    objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
                    paramJson = objectMapper.writeValueAsString(jsonParams);

                    if (StringUtils.isNotBlank(paramJson)) {
                        Context.getContext().setAttribute("httpJsonRequestParam", jsonRequestParam);
                        JsonRequestWrapper jRequestWrapper = new JsonRequestWrapper(req);
                        return jRequestWrapper;
                    }
                } catch (JsonProcessingException e) {
                    paramJson = "";
                    log.error("---- JsonProcessingException", e);
                }
            }
        }

        return req;
    }

    /***
     * 获取数据流 ，  注意： 数据流只能读取一次   (拿取到前端给出的json)
     * */
    public String requestInputStream(HttpServletRequest req) {
        String jsonParam = "";
        ServletInputStream inputStream = null;
        try {
            int contentLength = req.getContentLength();
            if (contentLength > 0) {
                byte[] buffer = new byte[contentLength];
                inputStream = req.getInputStream();
                for (int i = 0; i < contentLength; ) {
                    int len = inputStream.read(buffer, i, contentLength);
                    if (len == -1) {
                        break;
                    }
                    i += len;
                }
                jsonParam = new String(buffer, "utf-8");
            }
        } catch (IOException e) {
            log.error("requestInputStream: 参数获取成 error:", e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("requestInputStream: 参数获取成 error:", e);
                }
            }
        }
        return jsonParam;
    }
}
